Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessibility: Fix talkback after EoY modal bottom sheet is hidden #630

Merged
merged 2 commits into from
Dec 6, 2022

Conversation

ashiagr
Copy link
Contributor

@ashiagr ashiagr commented Dec 5, 2022

Description

This PR attempts to fix talkback issues after End of Year launch modal bottom sheet is shown.

From the issue description:

The only way to navigate is to use the swipe left/right on the edge of the screen gestures.

It felt strange that we could navigate through talkback by swiping on the x-axis, which means after the modal was dismissed, talkback focus was moved from the bottom sheet to the screen elements. I wonder if this behavior was due to one of the compose modal bottom sheet issues (1, 2).

As a workaround, I remove EoY compose modal bottom sheet once it is hidden. And re-add it when it is needed again with minimum changes to the existing logic.

Warning
PR targets release/7.28

Fixes #621

Testing Instructions

  • Fresh install the app
  • Build a listening history of at least 5 mins
  • Restart the app
  • When EoY launch modal is shown, dismiss it
  • Turn on talkback
  • Tap on screen elements
  • Notice that talkback works properly

Screenshots or Screencast

talkback.mp4

@ashiagr ashiagr requested a review from a team as a code owner December 5, 2022 10:23
@ashiagr ashiagr added this to the 7.28 ❄️ milestone Dec 5, 2022
@ashiagr ashiagr changed the title Accessibility: Fix talkback after compose modal bottom sheet is hidden Accessibility: Fix talkback after EoY modal bottom sheet is hidden Dec 5, 2022
Copy link
Contributor

@mchowning mchowning left a comment

Choose a reason for hiding this comment

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

This works great. I had one code question, but it is more for my learning than anything. Great job figuring out this fix. It's so weird that the modal was messing up TalkBack like this.

Comment on lines 58 to 77
LaunchedEffect(Unit) {
snapshotFlow { sheetState.currentValue }
.collect {
if (sheetState.currentValue == ModalBottomSheetValue.Expanded) {
onExpanded.invoke()
} else if (sheetState.currentValue == ModalBottomSheetValue.Hidden) {
if (isSheetShown) {
/* Remove bottom sheet from parent view when bottom sheet is hidden
on dismiss or back action for talkback to function properly. */
parent.removeAllViews()
} else {
if (!sheetState.isVisible && shouldShow) {
/* Show bottom sheet when it is hidden on initial set up */
displayBottomSheet(coroutineScope, sheetState)
isSheetShown = true
}
}
}
}
}
Copy link
Contributor

@mchowning mchowning Dec 5, 2022

Choose a reason for hiding this comment

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

Why do we need to create a snapshot flow instead of just passing sheetState.currentValue as the argument to the LaunchedEffect instead of unit?

LaunchedEffect(sheetState.currentValue) {
    if (sheetState.currentValue == ModalBottomSheetValue.Expanded) {
        // ...
    }
}

What you have here works fine, so I don't think this needs to change. I'm just asking because I suspect I would have used the LaunchedEffect-with-a-key approach, and I'm curious if maybe there's a gotcha I'm overlooking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

snapshotFlow allowed me to observe bottom sheet visibility changes and react to show/hide actions without having to cancel and re-launch LaunchEffect. Your suggestion works as well, and tbh I don't have a clear answer at this point about which one has a clear advantage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just came across this discussion, I haven't tested it though:

LaunchedEffect(someMutableState.value) will cause a custom Layout() in the same @composable function to remeasure every time the value of the key changes

generally snapshotFlow is more efficient for observing state in coroutines because it lets you avoid recompositions.

Copy link
Contributor

Choose a reason for hiding this comment

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

That is interesting. Sounds like the gist of it is that reading a value in the key field of a LaunchedEffect does make that composable scope depend on that value, and it will recompose that entire scope anytime the key changes, even if the LaunchedEffect does not generate any UI.

Thanks for pointing that out. I still have so much to learn with compose!

@ashiagr ashiagr merged commit a5b3738 into release/7.28 Dec 6, 2022
@ashiagr ashiagr deleted the fix/621-talkback branch December 6, 2022 06:51
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