[Predictive Back] Enhance in-app back animations#5026
Conversation
Generated by 🚫 Danger |
There was a problem hiding this comment.
Pull request overview
This PR implements predictive back gesture animations throughout the Pocket Casts Android app to provide smooth Material Design-compliant navigation experiences. The implementation includes custom transition helpers, comprehensive testing, and integration across fragments, activities, and navigation systems.
Changes:
- Added new utility classes (
PredictiveBackAnimator,PredictiveBackTransition,ComposeNavigationBackHelper,ActivityBackHelper) to handle predictive back animations with scale and fade effects - Integrated predictive back animation support into
BaseFragmentandBaseDialogFragmentwith customizable behavior via hooks - Split
BookmarksContainerFragmentinto two specialized fragments (BookmarksDialogFragmentfor Player modal context,BookmarksNavigationFragmentfor Profile navigation context) to resolve navigation conflicts - Enhanced
BottomNavigatorwith methods to support fragment peek animations during back gestures - Added comprehensive unit tests for all new helper classes
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| PredictiveBackAnimator.kt | New utility providing scale/fade animation methods for back gestures with hardware layer optimization |
| PredictiveBackTransition.kt | New transition manager for seekable AndroidX transitions during predictive back |
| PredictiveBackExtensions.kt | Extension functions for Compose navigation back handling |
| ComposeNavigationBackHelper.kt | Helper object for Compose navigation backstack management |
| ActivityBackHelper.kt | Helper for setting up predictive back on activities with various configurations |
| BaseFragment.kt | Added default predictive back animation support with customizable hooks |
| BaseDialogFragment.kt | Added predictive back animation for bottom sheet dialogs |
| MainActivity.kt | Integrated predictive back for bottom navigator and player sheet with coordinated animations |
| BottomNavigator.kt | Added previousFragment() and animation helper methods |
| StackOfStacks.kt | Added peekValueBelowTop() for fragment peek support |
| BookmarksDialogFragment.kt | Refactored from BookmarksContainerFragment for modal use case |
| BookmarksNavigationFragment.kt | New fragment for navigation context from Profile |
| AccountActivity.kt | Added PredictiveBackTransition usage |
| WebViewActivity.kt | Added predictive back animation |
| StoriesActivity.kt | Added Compose-based predictive back handling |
| PodcastFragment.kt | Calls notifyBackstackChanged() when entering/exiting multi-select mode |
| Test files | Comprehensive unit tests for all new helper classes |
| Build files | Added androidx.transition:transition-ktx dependency |
Project dependencies changeslist+ New Dependencies
androidx.transition:transition-ktx:1.6.0tree++--- com.automattic.tracks:crashlogging:6.0.7
+| \--- io.sentry:sentry-compose-android -> 8.18.0
+| \--- androidx.compose.material3:material3:1.2.1 -> 1.4.0
+| \--- androidx.compose.material3:material3-android:1.4.0
+| \--- androidx.activity:activity-compose:1.8.2 -> 1.12.4
+| \--- androidx.compose.ui:ui:1.0.1 -> 1.10.0
+| \--- androidx.compose.ui:ui-android:1.10.0
+| \--- androidx.transition:transition:1.6.0
+| \--- androidx.transition:transition-ktx:1.6.0 (c)
++--- project :modules:features:account
+| +--- project :modules:features:search
+| | \--- project :modules:services:views
+| | \--- androidx.transition:transition-ktx:1.5.1 -> 1.6.0
+| | +--- androidx.transition:transition:1.6.0 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib -> 2.3.10 (*)
+| | +--- androidx.transition:transition:1.6.0 (c)
+| | \--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.3.10 (c)
+| \--- androidx.transition:transition-ktx:1.5.1 -> 1.6.0 (*)
+\--- project :modules:features:navigation
+ +--- androidx.compose:compose-bom:2025.12.01 (*)
+ \--- project :modules:services:views (*) |
There was a problem hiding this comment.
I only tested it for now. I'll do the code review later. I found a couple of things that I think don't work well:
- The predictive animation for the player sheet appears a bit janky. Notice how it jumps and loses the fade transition especially at the top of the screen. It may be better to remove it in this context.
| Before | After |
|---|---|
before.mp4 |
after.mp4 |
- The initial scaling of the previous fragment on the back stack feels visually abrupt.
screen-20260223-220909-1771880933121.mp4
- Predictive back doesn't work for me during the onboarding flow. I don't think it is necessary to add it there, especially given how convoluted the code is. I'm highlighting it just in case.
I also found two bugs (likely the same underlying issue occurring in two places) that aren't related to this change, but probably to earlier predictive back integration work. When opening both Referrals and Up Next from the mini player, back navigation is not handled correctly and instead closes the app. We should fix this in a separate PR before it reaches beta.
screen-20260223-220618-1771880761173.mp4
|
If this is implemented I wouldn't be against a setting for fast, medium, long animations & no animations, some people like an instant response |
|
thanks @MiSikora, i've ticketed the bug you discoered here https://linear.app/a8c/issue/PCDROID-466/bug-broken-back-nav as it doesn't seem to be an easy/obvious one. I already sinked some time into it |
2ce292c to
cc3e66b
Compare
|
@MiSikora
I've disabled the custom transition on the player bottom sheet, so now you should see the default one.
Polished the animation params so now it seems to be smoother to my eyes.
Yeah, it doesn't work for me either, already spent some time on it without success. I'd lean towards just letting it go 👋 Please check the app again and review the code if you feel like these efforts are justified. |
MiSikora
left a comment
There was a problem hiding this comment.
I know a substantial amount of thoughtful work and research went into this change. However, after additional testing, I am concerned that it may frustrate more users than it benefits. If you would like to proceed, I strongly recommend distributing a prototype build to the team, particularly the designers, for validation before merging.
On iOS, the animations feel more natural. For example, Settings pages transition with a horizontal scroll. In our implementation, the current screen fades out completely before the previous screen appears. With gesture navigation, this looks ok(ish?). However, when using Up navigation or 3 button navigation, the transition feels abrupt and visually disruptive.
| iOS | Android |
|---|---|
ios.mov |
screen-20260226-163956-1772120376263.mp4 |
Polished the animation parameters so now it seems to be smoother to my eyes.
It still feels abrupt to me in practice, especially during rapid navigation. The issue is most noticeable on screens with a lot of content such as Discover or Podcasts. Would it be possible to remove the background fragment scaling while retaining the improved fade transition for the foreground fragment?
| @MainThread | ||
| fun finish() { | ||
| try { | ||
| seekController?.animateToEnd() |
There was a problem hiding this comment.
❓ Should the seekControler be cleared here?
| @MainThread | ||
| fun cancel() { | ||
| try { | ||
| seekController?.currentFraction = 0f |
There was a problem hiding this comment.
❓ Should the seekControler be cleared here?
| * Starts a seekable transition with scale and fade effects. | ||
| */ | ||
| @MainThread | ||
| fun start(backEvent: BackEventCompat) { |
There was a problem hiding this comment.
ℹ️ We can remove backEvent as it isn't used in the implementation.
| * Tests the seekable transition-based animations for predictive back gestures. | ||
| * Note: These are basic smoke tests since TransitionSeekController requires a real Android environment. | ||
| */ | ||
| class PredictiveBackTransitionTest { |
There was a problem hiding this comment.
PredictiveBackTransition is a thin wrapper around TransitionSeekController. The tests verify that calling the wrapper methods does not throw. That’s a very low-signal assertion and doesn’t give us meaningful correctness of behavior.
In general, tests are most valuable when they validate observable behavior or interactions. Otherwise, they add maintenance cost without increasing confidence in the implementation.
I’d lean toward removing them. If there’s particular coverage they’re meant to provide, I’m happy to revisit.
| import androidx.activity.OnBackPressedCallback | ||
| import androidx.navigation.NavController | ||
|
|
||
| object ActivityBackHelper { |
There was a problem hiding this comment.
❓ I’m not sure where this feedback best fits, so I’ll leave it here.
ActivityBackHelper, ComposeNavigationBackHelper, and PredictiveBackExtensions all appear to provide a similar category of functionality, but their API shapes are quite different.
ActivityBackHelperis both an object with functions and a set of extension functions that delegate to that object. And that object has extension functions inside as well.ComposeNavigationBackHelperis an object exposing functions.PredictiveBackExtensionsis a collection of extension functions.
Why they don’t follow a consistent design approach? Is there a specific constraint or usage pattern that drove these differences? If not, it may be worth aligning them to a single, consistent API style to reduce cognitive overhead and improve discoverability.
|
|
|
| App Name | ⌚ Wear | |
| Build Type | DebugProd | |
| Build Number | 9409 | |
| Version | 8.6-rc-2 | |
| Application ID | au.com.shiftyjelly.pocketcasts | |
| Commit | cc3e66b | |
| Direct Download | pocketcasts-wear-prototype-build-pr5026-cc3e66b.apk | |
| Installation URL | 1c00isj52598o |
📲 You can test the changes from this Pull Request in 🚗 Automotive by scanning the QR code below to install the corresponding build.
| App Name | 🚗 Automotive | |
| Build Type | DebugProd | |
| Build Number | 9409 | |
| Version | 8.6-rc-2 | |
| Application ID | au.com.shiftyjelly.pocketcasts | |
| Commit | cc3e66b | |
| Direct Download | pocketcasts-automotive-prototype-build-pr5026-cc3e66b.apk | |
| Installation URL | 1c00isj52598o |
|
Version |
|
Version |
|
We agreed not to include these animations in the app. internal discussion: p1773860264187239/1772474323.176949-slack-C05RR9P9RAT |

Description
This PR adds the same animations that the system uses for predictive back. I did a thorough research on this topic and it turned out that we can't rely the system's predictive back animation when navigating back from our screens, because:
-- Fragment navigation → Scale + fade with peek
-- Player bottom sheet → Scale to 80% then collapse
-- Bottom sheets (UpNext) → System collapse animation (can use default)
-- Modals → Custom slide animations
-- Selection mode → No animation, just exit mode
So the only viable way forward was to create custom transitions and hook them up on the appropriate places.
I noticed a nasty bug that took a lot of time to get rid of. It was specific to the Profile>Bookmarks screen. I had to
split it into two (BookmarksDialogFragment for Player, BookmarksNavigationFragment for Profile).
Why? The original BookmarksContainerFragment was trying to be both a bottom sheet dialog (modal context) and a navigation fragment (backstack context). This caused navigation confusion - the back gesture didn't know which behavior to use. Separating them made each context explicit and predictable.
Fixes PCDROID-462
Testing Instructions
Smoke test the app, pay attention to the back navigation.
Screenshots or Screencast
Screen_recording_20260223_111918.mp4
Checklist
./gradlew spotlessApplyto automatically apply formatting/linting)modules/services/localization/src/main/res/values/strings.xml