Multiple Instances of Screens and Actions Triggered by Rapid Taps #1941

Closed
opened 2025-11-26 23:02:30 -06:00 by GiteaMirror · 6 comments
Owner

Originally created by @qwexter on GitHub (Jul 24, 2024).

Bitwarden Beta

  • I'm using the new native Bitwarden Beta app and I'm aware that legacy .NET app bugs should be reported in bitwarden/mobile

Steps To Reproduce

Open the Bitwarden Android app.
Rapidly tap any navigation button multiple times.
Observe that multiple instances of the same screen are opened.
Navigate back through the screens. Note the occurrence of empty screens or broken navigation requiring an app restart.
Perform an action that involves a network request (e.g., synchronization).
Rapidly initiate the action multiple times.
Observe that multiple network requests are sent.

Expected Result

The app should process single events only once, preventing multiple instances of the same screen or multiple network requests from being triggered.

Actual Result

Multiple instances of the same screen can be opened, and multiple network requests can be sent, leading to broken navigation and potential app instability.

Screenshots or Videos

Additional Context

The Bitwarden Android app allows users to perform the same action multiple times when rapidly tapping buttons. This is particularly noticeable with navigation buttons, where multiple instances of the same screen can be opened. Navigating back through these screens can lead to empty screens or broken navigation, sometimes requiring a restart of the app. This issue also occurs with actions involving network requests, such as synchronization, resulting in multiple requests being sent.

This problem stems from how the app handles user events within the MVI and Compose architecture. It affects the entire app and should ideally be addressed at the architectural level.

Build Version

Version 2024.6.0

Environment Details

  • Android Emulator, Pixel 8, API 33
  • Reakme c35, API 32

Issue Tracking Info

  • I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
Originally created by @qwexter on GitHub (Jul 24, 2024). ### Bitwarden Beta - [X] I'm using the new native Bitwarden Beta app and I'm aware that legacy .NET app bugs should be reported in [bitwarden/mobile](https://github.com/bitwarden/mobile) ### Steps To Reproduce Open the Bitwarden Android app. Rapidly tap any navigation button multiple times. Observe that multiple instances of the same screen are opened. Navigate back through the screens. Note the occurrence of empty screens or broken navigation requiring an app restart. Perform an action that involves a network request (e.g., synchronization). Rapidly initiate the action multiple times. Observe that multiple network requests are sent. ### Expected Result The app should process single events only once, preventing multiple instances of the same screen or multiple network requests from being triggered. ### Actual Result Multiple instances of the same screen can be opened, and multiple network requests can be sent, leading to broken navigation and potential app instability. ### Screenshots or Videos <img src="https://github.com/user-attachments/assets/ab7f48de-438a-4aa0-a36f-415c8f9676d8" width=20% height=20%> ### Additional Context The Bitwarden Android app allows users to perform the same action multiple times when rapidly tapping buttons. This is particularly noticeable with navigation buttons, where multiple instances of the same screen can be opened. Navigating back through these screens can lead to empty screens or broken navigation, sometimes requiring a restart of the app. This issue also occurs with actions involving network requests, such as synchronization, resulting in multiple requests being sent. This problem stems from how the app handles user events within the MVI and Compose architecture. It affects the entire app and should ideally be addressed at the architectural level. ### Build Version Version 2024.6.0 ### Environment Details - Android Emulator, Pixel 8, API 33 - Reakme c35, API 32 ### Issue Tracking Info - [X] I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
GiteaMirror added the app:password-managerbug labels 2025-11-26 23:02:30 -06:00
Author
Owner

@bitwarden-bot commented on GitHub (Jul 24, 2024):

Thank you for your report! We've added this to our internal board for review.
ID: PM-10075

@bitwarden-bot commented on GitHub (Jul 24, 2024): Thank you for your report! We've added this to our internal board for review. ID: PM-10075
Author
Owner

@mak1nt0sh commented on GitHub (Aug 7, 2024):

To solve the issue of duplicate navigation events, especially during rapid interactions or animations, you can implement a safety check using the current NavBackStackEntry's lifecycle state. This approach is inspired by the Jetsnack sample from the Compose samples repository.

  1. Add a lifecycle check extension function to NavBackStackEntry:
/**
 * If the lifecycle is not resumed it means this NavBackStackEntry already processed a nav event.
 *
 * This is used to de-duplicate navigation events.
 */
fun NavBackStackEntry.lifecycleIsResumed() =
    this.lifecycle.currentState == Lifecycle.State.RESUMED
  1. Create a safe navigation extension function for NavController:
fun NavController.safeNavigate(
    route: String,
    navOptions: NavOptions? = null,
    navigatorExtras: Navigator.Extras? = null
) {
    val navBackStackEntry = currentBackStackEntry
    if (navBackStackEntry?.lifecycleIsResumed() == true) {
        navigate(route, navOptions, navigatorExtras)
    }
}
  1. Use safeNavigate instead of navigate for all navigation actions.

This solution ensures that navigation events are only processed when the current NavBackStackEntry is in the RESUMED state, effectively preventing duplicate navigations during animations or rapid interactions.

For more context, you can refer to the implementation in the Jetsnack sample:
081721ad44/Jetsnack/app/src/main/java/com/example/jetsnack/ui/JetsnackAppState.kt (L146)

For handling double-click issues in non-navigation events, you might want to consider this:
https://android-review.googlesource.com/c/platform/frameworks/support/+/2944594

@mak1nt0sh commented on GitHub (Aug 7, 2024): To solve the issue of duplicate navigation events, especially during rapid interactions or animations, you can implement a safety check using the current `NavBackStackEntry`'s lifecycle state. This approach is inspired by the Jetsnack sample from the Compose samples repository. 1. Add a lifecycle check extension function to `NavBackStackEntry`: ```kotlin /** * If the lifecycle is not resumed it means this NavBackStackEntry already processed a nav event. * * This is used to de-duplicate navigation events. */ fun NavBackStackEntry.lifecycleIsResumed() = this.lifecycle.currentState == Lifecycle.State.RESUMED ``` 2. Create a safe navigation extension function for `NavController`: ```kotlin fun NavController.safeNavigate( route: String, navOptions: NavOptions? = null, navigatorExtras: Navigator.Extras? = null ) { val navBackStackEntry = currentBackStackEntry if (navBackStackEntry?.lifecycleIsResumed() == true) { navigate(route, navOptions, navigatorExtras) } } ``` 3. Use `safeNavigate` instead of `navigate` for all navigation actions. This solution ensures that navigation events are only processed when the current `NavBackStackEntry` is in the RESUMED state, effectively preventing duplicate navigations during animations or rapid interactions. For more context, you can refer to the implementation in the Jetsnack sample: https://github.com/android/compose-samples/blob/081721ad44dfb29b55b1bc34f83d693b6b8dc9dd/Jetsnack/app/src/main/java/com/example/jetsnack/ui/JetsnackAppState.kt#L146 For handling double-click issues in non-navigation events, you might want to consider this: https://android-review.googlesource.com/c/platform/frameworks/support/+/2944594
Author
Owner

@michaldrabik commented on GitHub (Aug 10, 2024):

There's also 1st line of defense against this by simply debouncing taps by 300-500ms. It's just on UI level but we usually combine this with navigation guards proposed above since there might be some actions not related to nav.

@michaldrabik commented on GitHub (Aug 10, 2024): There's also 1st line of defense against this by simply debouncing taps by 300-500ms. It's just on UI level but we usually combine this with navigation guards proposed above since there might be some actions not related to nav.
Author
Owner

@closebot-bw commented on GitHub (Aug 12, 2025):

⚠️ Stale Issue Notice

This issue has been automatically marked as stale due to inactivity. It will be closed in 2 weeks (August 26, 2025) if no further activity occurs.

If this issue is still relevant and you would like to keep it open, please:

  • Comment on this issue to show continued interest
  • Provide any additional information or updates
  • Confirm that the issue still exists in the latest version

Thank you for your contribution to this project! 🙏

@closebot-bw commented on GitHub (Aug 12, 2025): ⚠️ **Stale Issue Notice** This issue has been automatically marked as stale due to inactivity. It will be closed in **2 weeks** (August 26, 2025) if no further activity occurs. If this issue is still relevant and you would like to keep it open, please: - Comment on this issue to show continued interest - Provide any additional information or updates - Confirm that the issue still exists in the latest version Thank you for your contribution to this project! 🙏
Author
Owner

@closebot-bw commented on GitHub (Aug 23, 2025):

🔔 Final Notice - Issue Will Be Closed Soon

This issue was previously marked as stale and will be automatically closed in 3 days (August 26, 2025) if no further activity occurs.

If you're still experiencing this issue or believe it should remain open, please comment below to prevent automatic closure.

We appreciate your understanding and contribution to keeping our issue tracker organized! 📋

@closebot-bw commented on GitHub (Aug 23, 2025): 🔔 **Final Notice - Issue Will Be Closed Soon** This issue was previously marked as stale and will be automatically closed in **3 days** (August 26, 2025) if no further activity occurs. If you're still experiencing this issue or believe it should remain open, please comment below to prevent automatic closure. We appreciate your understanding and contribution to keeping our issue tracker organized! 📋
Author
Owner

@closebot-bw commented on GitHub (Aug 26, 2025):

🔒 Issue Closed Due to Inactivity

This issue has been automatically closed due to lack of activity for an extended period. We periodically review and close inactive issues to help maintain our issue tracker and focus on current priorities.

If this issue is still relevant:

  • Please create a new issue with updated information
  • Include steps to reproduce the problem if it's a bug report
  • Mention if this issue still occurs in the latest version

Thank you for your contribution to this project. Your feedback helps us improve! 🙏

@closebot-bw commented on GitHub (Aug 26, 2025): 🔒 **Issue Closed Due to Inactivity** This issue has been automatically closed due to lack of activity for an extended period. We periodically review and close inactive issues to help maintain our issue tracker and focus on current priorities. **If this issue is still relevant:** - Please create a new issue with updated information - Include steps to reproduce the problem if it's a bug report - Mention if this issue still occurs in the latest version Thank you for your contribution to this project. Your feedback helps us improve! 🙏
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/android#1941