More Accessible Graphs with Jetpack Compose Part 2: Adding Keyboard Interaction
Tags: a11y, android, mobile, jetpackcompose
Published: 2023-08-03 03:04:54

Welcome to the second episode of "More Accessible Graphs with Jetpack Compose" - in this blog post, we'll continue from where we left off in the first one. We will look at improving the (physical) keyboard access for graphs in Jetpack Compose. Link to the first part: [More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description][1]

At the end of the blog post, I mentioned that the second part would be about keyboard and switch access. After testing my code for the blog post, I realized that the switch access part didn't work. After some deep-diving into documentation, I admitted I wouldn't add it to this post as it would require a lot of research. So there likely will be at least four parts in this "More Accessible Graphs with Jetpack Compose"-blog post series.

In the first blog post, we added a `Higlighter`-component, an overlay on top of the line graph I had built. Then we made it focusable and added a content description for accessibility services. 

After these changes, a keyboard user can already focus on the different parts of the graph, but there's one problem: The graph legend is not visible. And you may now wonder if it is really a problem - we added the content description, right?

The content description is unavailable if you only use a keyboard. If a person, who can see and uses a hardware device for navigation, navigates with the graph in the current state, they probably get frustrated because this is what they would get:

<video controls class="portrait-video">
  <source src="//videos.ctfassets.net/mpqufjsy02zr/7jdl1GxtBF2FhxvGpQXJp1/395ee860ee3f62c52b14e742f2296d29/keyboard-start-en.mp4" type="video/mp4" />  
</video>

They can see that the focus changes but can't see the individual values. If I encountered something like this, I would think there is a bug in the app. 

## Adding Keyboard Interaction

Users can already focus on each section, so we no longer need to worry about that. But we'll need a way to change the currently focused position on the x-axis - like if a user was moving their finger on the graph. 

The `highlightedX`-parameter is the one that controls the currently highlighted position on the x-axis. We'll need to add a way to change it programmatically when a highlighter section gets focus. Let's first add a function to do that: 

```kotlin
@Composable
fun Highlighter(
    ...
    setFocus: (Float) -> Unit
) { ... }
```

On the parent component, which stores `highlightedX,` we define a function to change the value of highlighted X-position:

```kotlin
Highlighter(
    ...
    setFocus = { newX ->
        highlightedX = newX
    }
)
```

Okay, now we have a function to control the x-position and switch the currently focused position on the x-axis. We want to pass the x-position of the currently focused highlighter section to the function, and for that, we attach an `onFocusChanged`-modifier.

```kotlin
.onFocusChanged {
    if (it.isFocused) {
        setFocus(point.x)
    }
}
```

Within the modifier, we check if the current section is focused, and if it is, we use the `setFocus`-function, passing the x-position of that currently focused section. 

After this change, we already have working code - focusing on a highlighter section that shows the graph label so that a user can see the values for each point on that focused year.

There is just a minor tweak we still want to do. Right now, the border of the highlighter is visible if there is a focus on the component from both hardware devices and pointer input. We don't want to show the highlighter component's border for pointer input because there is already a different highlighter (a dashed line) when a user uses pointer input.

We will fix that by adding a state for border color, setting it to transparent by default, and then updating it when the focus changes. 

```kotlin
var color by remember { mutableStateOf(Color.Transparent) }
...
// Then on .onFocusChanged-modifier:

.onFocusChanged {
    color = if (it.isFocused) focusedColor else Color.Transparent
    ...
}
```
And we use that `color`-variable for the border color for each highlighter section. 

Now it all works as expected, as seen in this video:

<video controls class="portrait-video">
  <source src="//videos.ctfassets.net/mpqufjsy02zr/276754Rs7AbUSxMkUCYoyS/cd83be97e1d48708bff21209c756cc59/keyboard-improved-en.mp4" type="video/mp4" />  
</video>

The video shows that focus and the label text change when a user navigates by using a keyboard.

[You can find all the changes made in this commit.][2]

## Wrapping Up

In this blog post, we've developed the `Highlighter`-component further to allow interaction with assistive devices like hardware keyboards. This way, a user won't be relying only on being able to use pointer input, such as touch. 

The third blog post will be about differentiating data with other means than color. 

## Links in the Blog Post

- [More Accessible Graphs with Jetpack Compose Part 1: Adding Content Description][1]
- [You can find all the changes made in this commit.][2]

[1]: https://dev.to/eevajonnapanula/more-accessible-graphs-with-jetpack-compose-part-1-adding-content-description-43be
[2]: https://github.com/eevajonnapanula/graph-accessibility-example/commit/5e98304d1d82b53eea9241c3c8bd7a7b1943a1d4