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

Improve iOS a11y sync behavior #1170

Merged
merged 15 commits into from
Mar 8, 2024
Merged

Conversation

elijah-semyonov
Copy link

Proposed Changes

  1. Implement desktop-like sync logic. The render can issue undefined amount of layout and semantics invalidation, that will be coalesced into a single sync every time it happens.

  2. Implement different logic based on kind of invalidation to avoid extra when only bounds are changed.

  3. Fix wrong refocusing logical bug.

Testing

Test: N/A

@elijah-semyonov elijah-semyonov marked this pull request as draft March 7, 2024 10:05
@elijah-semyonov
Copy link
Author

I'll do a minor refactor.

@elijah-semyonov elijah-semyonov marked this pull request as ready for review March 7, 2024 10:34
* A set of node ids that had their bounds invalidated after the last sync.
*/
private var invalidatedBoundsNodeIds = mutableSetOf<Int>()
private val invalidationChannel = Channel<Unit>(1, onBufferOverflow = BufferOverflow.DROP_LATEST)
Copy link

Choose a reason for hiding this comment

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

This should be Channel<Unit>(Channel.CONFLATED). It's the same thing, but clearer. I'll change in on the desktop side too.

Copy link
Author

@elijah-semyonov elijah-semyonov Mar 7, 2024

Choose a reason for hiding this comment

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

I've changed the implementation so this one is not relevant anymore

val updateIntervalMillis = 50L
// TODO: this approach was copied from desktop implementation, obviously it has a [updateIntervalMillis] lag
// between the actual change in the semantics tree and the change in the accessibility tree.
// should we use some other approach?
coroutineScope.launch {
while (isAlive) {
Copy link

Choose a reason for hiding this comment

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

I don't think this isAlive is needed. When you cancel the Job, a CancellationException is thrown inside the coroutine.

Copy link
Author

Choose a reason for hiding this comment

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

It makes sense, will change it

Copy link
Author

Choose a reason for hiding this comment

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

Dome

coroutineScope.launch {
while (isAlive) {
var result: NodesSyncResult
var strategy = SemanticsTreeSyncStrategy.from(
Copy link

Choose a reason for hiding this comment

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

It's not necessary to send this data through the channel.

The channel is just used as a synchronization primitive to trigger the coroutine to awake. The information about what to do when awaking can just be put in a regular data structure by onSemanticsChange and onLayoutChange.

Copy link
Author

Choose a reason for hiding this comment

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

This approach has more straightforward data-flow though instead of scattering the states all around the class.

Copy link

Choose a reason for hiding this comment

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

It could affect performance, and IMO is not a good idea in general (mixing a synchronization utility with logic). Tomorrow there could be a better/faster way to trigger a coroutine to wake up (like Java's notify) that doesn't allow data to pass through.

But your call.

Copy link
Author

Choose a reason for hiding this comment

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

So you'd insist to revert e8de6ee?

Copy link
Author

Choose a reason for hiding this comment

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

Reverted

Copy link

Choose a reason for hiding this comment

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

I don't insist, just suggesting.

Comment on lines 1000 to 1004
while (true) {
// Consumes all the invalidations to select a proper strategy
val invalidation = invalidationChannel.tryReceive().getOrNull() ?: break
strategy = strategy.reduce(invalidation)
}
Copy link
Member

Choose a reason for hiding this comment

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

Extract this into a helper function (channel ext)

Copy link
Author

@elijah-semyonov elijah-semyonov Mar 7, 2024

Choose a reason for hiding this comment

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

I'd like to keep this code inline so the entire suspend -> consumeRemaining sequence is visible to a reader

Copy link
Member

Choose a reason for hiding this comment

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

How is "suspend" related here?

Copy link
Author

Choose a reason for hiding this comment

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

The receive call is two lines above, it's suspending and is a delimiter for a synchronous inner buffer consumption.
My intent was to make this entire logic visible to a reader.

Copy link
Author

Choose a reason for hiding this comment

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

Reverted to pre-refactor

@elijah-semyonov elijah-semyonov merged commit 27f314e into jb-main Mar 8, 2024
6 checks passed
@elijah-semyonov elijah-semyonov deleted the es/modify-sync-behavior branch March 8, 2024 10:54
@elijah-semyonov elijah-semyonov changed the title iOS improve a11y sync behavior Improve iOS a11y sync behavior Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants