One of the best ways to learn is to jump right in and start applying your new knowledge. Throughout this book, you’ll be working on making an app more accessible. You’ll be able to apply the techniques you learn here right away, whether you’re working on an existing app or building a new one outside of this book. You don’t even need to finish the book to start applying the new information from each chapter in your projects!
You’ll soon see that you already have the tools to get started and that it takes only a couple of lines of code to make a big difference for your users.
Introducing Taco Tuesday
Taco Tuesday is the name of the app you’ll develop as you work through this book. It allows you to discover and save new recipes to enjoy on your next Taco Tuesday. Once you’ve found a recipe you like, you can indicate that you made it, rate it and add notes.
Find the starter project in the materials for this chapter and open it in Android Studio. Build and run.
You’ll see that there are auto-advancing slides followed by recipe cards at the on-boarding stage. You can swipe the recipe cards to the right and left to save for later or pass them up, respectively, and move to the next recipe.
Tap Favorites to see the saved recipes. To see a details screen that shows all the ingredients and instructions for a certain recipe, tap the View icon on the recipe card. From here, you can add your rating and notes if you’ve saved the recipe.
Finally, you’ll see some pop-up dialogs and a bare-bones settings screen.
You may have noticed the app has several unpleasant features. The pop-ups are disruptive and the on-boarding process is unclear.
As you improve the app’s accessibility, you’ll also improve the overall user experience. When you complete this book, the app will look more like this:
So much nicer! Without further adieu, it’s time to dig in and start improving this app!
Improving accessibility through linters
Do you feel lucky? Well, you are! Not only do you have this book in your hands, but you probably use one of the tools that can make your app more accessible: lint. Android linters include a category that’s all about supporting accessibility.
In most cases, Android Studio enables the lint rules by default. Before you proceed, though, make sure that they are really enabled by following these steps.
In Android Studio, go to Preferences ‣ Editor ‣ Inspections.
Pick the right profile at the top — there are some options:
- If you choose Project Default, you can check your lint rules into version control.
- Otherwise, if you pick Default, it will be your local rules. You can create more profiles if needed.
For this project, it doesn’t matter which profile you use, so just pick one.
Review the list of items below the profile selection. Look for Android ‣ Lint ‣ Accessibility. Select the checkbox at the end of each item.
On this screen, you set the lint severity. Select each accessibility lint and make sure the severity is Warning or Error.
Click OK to save the lint settings and close Preferences. Now you’re all set to see lint errors.
Seeing lint errors
You have some options for how to view lint errors. The first way is to use Gradle in the command line. Run this command if you’re on Linux or Mac:
Or this one for Windows:
Note: You can use the terminal window in Android Studio under View ‣ Terminal.
It might take a few minutes to complete. When it’s done, you’ll see in the output: Wrote HTML report to <filename>. Open that file in your browser to see a list of lint warnings.
Scroll down until you see Usability, Accessibility, and Internationalization. These are the warnings that matter for this book.
Note: If you see lint errors as raw text, make sure that filename has the
To learn more about each warning, including its location in the code, click the hyperlink. Start by selecting ContentDescription.
The second way to see the results is right in your code. In the HTML output, you can see that
contentDescription is missing from fragment_discover.xml. Go to that file and look at the
discover_button_discard. Hover your mouse over the
Phew! This is app is a bit of a mess with all these warnings. Good thing you’re here to fix it! The Android system does much of the work of supporting accessibility for you. Because of that, many of the changes you need to make take only a line or two of code.
Understanding content descriptions
contentDescription warning is one of the accessibility components. But what does it mean?
Assistive technologies, such as screen readers, use content descriptions to communicate what’s on the screen to a user. The system already knows how to read many views to the user. For example, it will read the text for a
TextView. Images are much harder to infer without your help.
By adding a
contentDescription to an image or image button, you help the user know what the item is and its purpose. In short, this description will literally tell the user that it is a “Save button”, so the user knows how to save their changes. Without a
contentDescription, the user would hear “unlabeled button”. You’ll learn more about these in Chapter 3, “Tools and Testing”.
There are quite a few descriptions to add. You’ll get your hands dirty with them over the next several pages.
Adding content descriptions
Open fragment_discover.xml. The first element you’ll fix is the topmost
ImageView with the
contentDescription warning that has the ÍD
discover_button_discard. This is the thumbs-down button on the first screen that you tap to pass on a recipe.
For items that act like buttons, like this one, it’s best to use a single verb to describe the view. For this view, use Discard because you’re discarding the recipe to look at the next. Lucky for you, there’s a
String resource named
shared_discard in use throughout the app!
contentDescription attribute to the
ImageView with the ID
This makes it so that accessibility services, and the people who use them, know to use this button to discard a recipe.
Notice that the description doesn’t contain the word button. The system is able to decipher when a component is clickable. The system communicates that state to the user. As a rule, you don’t need to use words like “button” or “label” in your descriptions.
One line makes a difference for the user experience!
Do the same for the Try it image. Add the following to the
ImageView with the ID
discover_button_try in the same file:
Good work! That’s one more view that accessibility services can correctly interpret. Now the user will understand this button saves the recipe to “Try it”.
Run the linter again and you’ll and see two fewer warnings — and you only needed two little lines!
Adding more content descriptions
You’re a pro at this now! See if you can work through the following content descriptions with minimal instruction. If you get stuck, check the solution in the final project for this chapter.
Find the views in the following files and add the given content descriptions:
In fragment_recipe_detail.xml, add the content description
Likewise, in item_try_it_recipe.xml, add the content description
ImageViewwith the ID
Great! You took care of two more views.
For now, make this your practice: Pick a verb for a button that performs an action. For other images, follow the pattern object + action + context. For example: “A person eating a taco on a Tuesday”.
In Chapter 4, “Perceivable: Layout and Labeling”, you’ll learn more about writing good content descriptions.
Programmatically adding content descriptions
Sorry to inform you, but there’s one issue with your changes to
recipe_detail_try_discard_button. Depending on whether you’ve saved the current recipe to try later or not, this view toggles between “Try it” and “Dismiss” in its behavior. Next, you’ll implement a bit of Kotlin code to fix this issue.
As with most view attributes, you can set the
contentDescription programmatically. Go to
RecipeDetailFragment to set this view. Find the two methods.
At the top of each of these, you already have the image set to thumbs-up or thumbs-down on
recipeDetailTryDiscardButton. This is where you’ll set the content description.
At the top of
showEditableFields(), add the following content description:
recipeDetailTryDiscardButton.contentDescription = getString(R.string.shared_discard)
Now, when the view is thumbs-down, the content description is “Discard”.
To do the same for the opposite state, add this to the top of
recipeDetailTryDiscardButton.contentDescription = getString(R.string.shared_try_it)
Great! Now, when the button changes state, the content description changes with it.
Run lint now. You’ll see fewer ContentDescription warnings — there should be three left. You’ll handle these slightly differently from the rest.
When you design for accessibility, you need to think about two types of content:
- The content you upload, which you can describe.
- The content the user uploads, which you can’t possibly know how to describe — unless you have supernatural abilities and can predict what future users will upload.
But seriously, how do you support accessibility for user-generated content? For example, in the app, each recipe has a different image. Eventually, these images will come from users, so you need to consider the problem now.
There are two standard solutions for this situation:
- Ask the user to describe the image. Then you can include that description in the API response and set it to
- Tell the accessibility services that this view isn’t important for it to read so it will skip the view.
The simplest answer is the second solution.
You already learned how to set a
String as the
contentDescription in the previous section, which you should do for the content you provide. This time, you’ll tell the system that it shouldn’t read the view by setting
ImageView with the ID
discover_recipe_image in fragment_discover.xml. This is the image that comes with the recipe.
@null as the
contentDescription for the view with ID
Perfect. Now the screen reader has the information it needs about this image.
Note: In Chapter 4, “Perceivable: Layout and Labeling”, you’ll learn about
importantForAccessibility, which sends a similar message but has slightly different behavior.
Now, run the linter. Oh, look! There’s another case where it feels uncertain about the content description text: decorative views.
Handling decorative views
Go to fragment_recipe_detail.xml and find a view with the ID
recipe_detail_divider. As the name suggests, this view is a thin line that acts as a divider between sections. What should the user know about this?
Because it’s a decorative view, it doesn’t add any meaning. You can safely ignore decorations using
android:contentDescription="@null". This guideline applies to elements like dividers or gradients.
You could add that here. However, you have another option!
This doesn’t need to be an
ImageView. Instead, you’ll set it as a plain old
View with the color as the background.
The final view should look like this:
<View android:id="@+id/recipe_detail_divider" android:layout_width="match_parent" android:layout_height="1dp" android:background="?colorControlHighlight" />
Notice that the linter warning doesn’t appear in Android Studio anymore. That’s because accessibility services skip these
View elements unless you signal that a view is important.
Throughout this book, you’ll learn some ways to tell the accessibility services how to treat a plain view. Content description is only one of them.
Run the linter again. There’s one
ContentDescription warning left. You’ll have the opportunity to fix it in the challenge at the end of this chapter.
But wait! You need to learn how to handle another common warning before you can call this chapter complete.
Fixing keyboard inaccessible widgets
When you run the linter, you’ll see KeyboardInaccessibleWidget amongst the warnings. Find and click it to go to the detailed list of files.
This warning happens when you declare a view as clickable but not focusable. When you do this, you make the view interactive for people who tap it to perform the action, but not someone who’s using a keyboard or similar technology to access it.
Often with keyboards, you first need to focus the view, then select it. If it’s not focusable, the user can’t perform the action. Again, this is something you can fix with only one line of code!
Open fragment_discover.xml, where all these warnings live. Find the
ImageView with the ID
discover_button_discard. Add the following property to the view:
And that’s all it takes! By adding the
focusable property, you inform the system to let people using a keyboard focus on it.
While you’re at it, add the same property to the other two views in this file that need it. These views have the IDs
Run the linter. You’ve solved so many accessibility warnings! There should be none left.
Build and run. It should behave the same as before when you’re not using accessibility services. In the next chapter, you’ll learn how to use accessibility services so you can experience the impact of your changes.
Now that you’ve gotten your hands dirty, and are hopefully ready to focus on some theory, this chapter will pivot to bring you up to speed on accessibility guidelines.
This book teaches you the best practices for and conforms to the Web Content Accessibility Guidelines (or WCAG) version 2.1 as the standard for accessibility. These guidelines are an industry standard.
WCAG is exactly as the name suggests: a guideline for making your web content or app accessible. The guidelines help you build your app so it’s consumable to a wide range of people with disabilities. It doesn’t address every user’s needs — nor does this book — but it covers the main points and best practices.
Even though these guidelines specify they are for “web”, they’re useful for Android engineers to follow, and here’s why:
- Used for legal requirements: Remember the legal requirements you learned about in Chapter 1, “Why Accessibility?” Many legal battles use WCAG as the guidelines for how to comply with accessibility laws across countries. That alone should make it a priority for you to learn.
- Common language with others: You can use these guidelines across platforms. This means that you can talk about accessibility using this language with iOS and web engineers, as well as customers, product managers and designers.
- Written with mobile in mind: As of version 2.1, WCAG includes requirements that apply to websites and mobile apps and tablets.
Note: As an example, the content descriptions you added help you satisfy the first requirement:
Guideline 1.1 Text Alternatives: Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language.
You’ll dig even deeper into this guideline in Chapter 4, “Perceivable: Layout and Labeling”.
Building a POUR app
WCAG uses a lovely little acronym to categorize the principles of the requirements:
- P erceivable
- O perable
- U nderstandable
- R obust
This book is organized by these principles, and you’ll learn more about each of them as you go. Let them serve as a mental checklist: “Is my app perceivable, operable, understandable and robust?”
Content descriptions fall under “perceivable”. They allow your user to perceive what’s in an image or understand a button’s purpose.
Making a view both clickable and focusable falls into the “operable” category. It lets people using a keyboard operate your app.
Measuring using levels of compliance
WCAG provides levels of conformance for each of its guidelines, which include A, AA, and AAA, going from lowest to highest. Level A conformance means you’ll meet most people’s needs The industry best practice is to meet A or AA.
Guideline 1.1 Text Alternatives, which covers content descriptions, is level A. It’s one of the first guidelines, and all apps should conform to it.
Note: This book will reference WCAG and the Android resources throughout. If you’re curious enough to look into these, head to WCAG at https://www.w3.org/TR/WCAG21/ and the Android docs resource at https://developer.android.com/guide/topics/ui/accessibility.
Choosing universal design
There are two predominant strategies for designing accessible apps: accessible design and universal design.
Accessible design is self-descriptive: It’s design that makes your app accessible. Features could be accessible in and of themselves, or you could create a tailored experience so someone using assistive technologies gets a different version of the app.
Universal design is a subset of accessible design, with the intent being to ensure that everyone gets the same experience. The design is accessible by default, so you don’t need to create a second design specifically for those using assistive technologies.
“The intent of universal design is to simplify life for everyone by making products, communications and the built environment more usable by as many people as possible at little or no extra cost. Universal design benefits people of all ages and abilities.”
-The Center for Universal Design at NC State University College of Design
This book uses universal design wherever possible because it makes your user experience consistent for all users, and it protects user privacy. With universal design, you don’t need to pry into your users’ data to see if they use assistive technologies.
At this early stage in the book, you’re already better equipped to start improving accessibility in any app! Before you move on to the next chapter, here’s an opportunity to practice what you’ve learned already.
Challenge 1: Solve the ContentDescription warning
Remember, the last time you ran the linter there was one more
ContentDescription warning. Now’s the time to show your stuff by solving it!
In item_onboarding.xml you’ll see an
ImageView with the ID
onboarding_image. This is the view with the linter warning. If you remember, the on-boarding screen has an image with each slide. How do you think these should behave?
For this challenge, solve the
ContentDescription warning on the on-boarding screen. You’ll need to decide whether to ignore it or add a description. If you get stuck, review the solution in the challenge project in the chapter materials.
Some tips and hints:
- To see the on-boarding screen again when you run the app, check Show on-boarding in the Settings screen. Then, next time you close and open the app you’ll see the on-boarding screen.
- If you decide to set the content description programmatically, set this view’s properties in
- Again, if you set the content description programmatically, make the warning go away in the XML file by either adding a placeholder content description or by suppressing the lint using
tools:ignore="ContentDescription". If you suppress the warning like this, it’s helpful to add a comment to clarify that it’s set programmatically.
- Android lint is a useful tool for identifying accessibility issues.
- One or two lines of code can often solve accessibility warnings.
- Content descriptions tell accessibility services what a given image is.
- To improve keyboard users’ experiences, add
android:focusable="true"to a view that you declare
- WCAG and the Android docs are excellent resources for understanding accessibility requirements.
- Choose universal design over accessible design so that everyone has the same experience in your app.
Congrats! You’re already making great improvements to the app. If you got stuck at any point, check out the final and challenge projects included in the chapter materials.
Think about the huge impact you’ve already made on this app. Before your changes, there was no straightforward way for a person using a screen reader to know how to save a recipe, and there would be no way for someone using a keyboard to dismiss a recipe to move to the next.
Before you can get much further with your new knowledge, you’ll need to learn more about the common assistive technologies people use and about testing technologies that can detect potential issues. Move on to the next chapter to fill in those gaps and move one step closer to being an accessibility detective!