Skip to content

Conversation

@tunjid
Copy link

@tunjid tunjid commented Nov 3, 2025

This PR add animations to TwoPaneScene to test out navigation event library.

IDK if this is in scope for this repo, will close if it isn't.

Rename sendEvents to awaitEvents

Update APIs to match the 1.0.0-beta01 release

Remove dependency on tunjid composables
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tunjid, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the navigation experience within TwoPaneScene by integrating a new navigation event library. It introduces interactive drag-to-pop gestures and a flexible split-pane layout, allowing for dynamic content display and animated transitions. The changes aim to provide a more modern and fluid user interface, especially when handling predictive back gestures and shared element animations across different screens.

Highlights

  • Navigation Event Library Integration: Introduced and integrated the androidx.navigationevent library to enable advanced navigation animations and predictive back gestures.
  • Custom Drag-to-Pop Gesture: Implemented a DragToPop composable and state management to allow users to dismiss screens using a drag gesture, enhancing interactive navigation.
  • Dynamic Two-Pane Layout: Developed SlideToPopLayout and SplitLayout to manage two-pane navigation dynamically, supporting draggable separators and adapting to window size changes.
  • Animated TwoPaneActivity: Enhanced TwoPaneActivity with SharedTransitionLayout for shared element transitions and integrated the new navigation event handling for animated and predictive back experiences, particularly for the profile screen.
  • Dependency Updates: Updated several Compose and Navigation-related dependencies to their latest beta versions and added the new navigationevent libraries.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces animations for TwoPaneScene using the new navigationevent library, including "drag to pop" and "slide to pop" gestures. The changes are quite extensive, adding new files for gesture handling and significantly refactoring TwoPaneScene and its strategy. The implementation demonstrates advanced Compose concepts like custom gestures, SharedTransitionLayout, and handling predictive back navigation events.

My review focuses on improving code safety, consistency, and readability. I've pointed out a few areas where non-null assertions can be replaced with safer checks, a magic number can be extracted to a constant, and unsafe type casts can be avoided to prevent potential runtime crashes. Overall, this is a great showcase of the new navigation event capabilities.

Comment on lines +212 to +222
private val Transition<*>.sceneTargetDestinationKey: TwoPaneSceneKey
get() {
val target = parentTransition?.targetState as Scene<*>
return target.key as TwoPaneSceneKey
}

private val Transition<*>.sceneCurrentDestinationKey: TwoPaneSceneKey
get() {
val target = parentTransition?.currentState as Scene<*>
return target.key as TwoPaneSceneKey
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of unsafe casts (as Scene<*> and as TwoPaneSceneKey) can lead to a ClassCastException if a different type of scene or key is encountered. This can make the code brittle, especially if other scene strategies are introduced. It's safer to use a safe cast (as?) and handle the potential null case. The call sites in isShowingBackContent already use null-safe access, so they should be compatible with this change.

Suggested change
private val Transition<*>.sceneTargetDestinationKey: TwoPaneSceneKey
get() {
val target = parentTransition?.targetState as Scene<*>
return target.key as TwoPaneSceneKey
}
private val Transition<*>.sceneCurrentDestinationKey: TwoPaneSceneKey
get() {
val target = parentTransition?.currentState as Scene<*>
return target.key as TwoPaneSceneKey
}
private val Transition<*>.sceneTargetDestinationKey: TwoPaneSceneKey?
get() {
val target = parentTransition?.targetState as? Scene<*>
return target?.key as? TwoPaneSceneKey
}
private val Transition<*>.sceneCurrentDestinationKey: TwoPaneSceneKey?
get() {
val target = parentTransition?.currentState as? Scene<*>
return target?.key as? TwoPaneSceneKey
}

Comment on lines +211 to +212
val navigationEventDispatcher = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency and safer code, it's better to use checkNotNull instead of the non-null asserted call (!!). This is already done in DragToPop.kt (line 115). checkNotNull makes the requirement explicit and can be more descriptive if it fails.

Suggested change
val navigationEventDispatcher = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
val navigationEventDispatcher = checkNotNull(LocalNavigationEventDispatcherOwner.current?.navigationEventDispatcher)

Comment on lines +167 to +168
val scale = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other parts of the code (e.g., DragToPop.kt) and for safer null handling, consider using checkNotNull instead of the non-null assertion (!!). This makes the code's intent clearer and can provide better error messages if the required component is missing.

Suggested change
val scale = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
val scale = checkNotNull(LocalNavigationEventDispatcherOwner.current?.navigationEventDispatcher)

Comment on lines +216 to +219
is NavigationEventTransitionState.InProgress -> max(
1f - latestEvent.progress,
0.7f
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The value 0.7f is a magic number representing the minimum scale during a predictive back gesture. To improve readability and maintainability, it's best to extract it into a named constant.

For example, you could define a file-level constant:

private const val MIN_PREDICTIVE_BACK_SCALE = 0.7f

And then use it here.

Comment on lines +167 to +168
val navigationEventDispatcher = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to other parts of the codebase, it's recommended to use checkNotNull instead of the non-null assertion (!!) for consistency and safer code. This helps in providing more descriptive error messages.

Suggested change
val navigationEventDispatcher = LocalNavigationEventDispatcherOwner.current!!
.navigationEventDispatcher
val navigationEventDispatcher = checkNotNull(LocalNavigationEventDispatcherOwner.current?.navigationEventDispatcher)

Copy link
Collaborator

@ianhanniballake ianhanniballake left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making a simple recipe more complicated, can you move this to a separate AnimatedTwoPaneScene recipe? That way developers can see how to progresively enhance their scene from a more simple example to a more complicated example, choosing the right level for their needs.

Please make sure to update the README to call out the new recipe as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants