Category: Accessibility

Set Up Android Accessibility Tests Using Espresso

Espresso Logo
The Espresso documentation has a good simple example of how to set up Accessibility Tests in Android. By including AccessibilityChecks, your tests run a number of rules against the activity/fragment under test to ensure it’s accessibility. The tests fail if all the rules do not pass. The basic gist is that you add a @BeforeClass annotated method which calls AccessibilityChecks.enable():

@BeforeClass
public static void enableAccessibilityChecks() {
    AccessibilityChecks.enable();
}

You are supposed to enable this in Espresso 3 by adding the following dependencies to your build.gradle file:


androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-accessibility:3.0.1'

Unfortunately, I have not been able to make it work due to an error in the Espresso library.

Espresso 3.0.1 Broken

The setup described in the Android Documentation results in a run-time error if you include the espresso-accessibility library referenced in the documentation:

Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Landroid/support/test/espresso/accessibility/R$attr;

This issue was reported on Stack Overflow, but the one answer did not work for me. In the Google Issue tracker a response implies the problem is fixed in v3.0.2. I was unable to get my hands on that version to test it out.

In order to solve the problem, I had to roll back the Espresso libraries to version 3.0.0 in build.gradle:

Espresso 3.0.0 Broken

Turns out this version of Espresso is also broken, but in a different way. It’s missing a transitive dependency on Guava. To get Espresso 3.0.0 to work, you need to add the missing dependency on Guava into your build.gradle:


androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.0'
androidTestImplementation 'com.android.support.test:runner:1.0.0'
androidTestImplementation 'com.android.support.test.espresso:espresso-accessibility:3.0.0'
androidTestImplementation 'com.google.guava:guava:20.0'

I published a simple example project demonstrating an Espresso UI test that includes Accessibility checks on Github. The project’s one UI test actually fails, so you can see what the output looks like when an accessibility check fails. There is a comment in activity_main.xml where the accessibility problem lies. The “broken” branch has the project set up for Espresso 3.0.1 so you can see that error. Hopefully Google pushes 3.0.2 soon.

Resources – Apps Users Want to Use

DetroitDevDay

I am presenting this talk at DetroitDevDay on November 12th. It’s a look into some differing views on psychology and app composition and how it affects users of software. As a mobile developer, this kind of work had come to the forefront as the bar for mobile apps continues to rise.

Books

Articles

Videos

Adding Keyboard Navigation to a RecyclerView

List showing selected tab only

I’ve created a list of items using a RecyclerView which works fine (see the screenshot). But, in order to keep my app accessible, I need to support keyboard-based navigation. Just using the normal Recyclerview pattern and the associated layout was not good enough. If I use a keyboard, I am unable to move the focus down into the list, the focus stops in the tab control and then cycles back up to the app bar. Tapping the items with your finger works fine, and even switch access moves the focus properly to the list items. But it doesn’t work for the keyboard.

The trick is to set the LinearLayout to focusable and clickable, and set the background of the LinearLayout to:

android:background="?android:attr/selectableItemBackground"

as shown below in the XML layout snippet below. These three changes allow the list items to be selectable by the keyboard.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:focusable="true"
    android:clickable="true"
    android:paddingBottom="5dp">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginTop="8dp"
        android:adjustViewBounds="true"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:paddingTop="12dp"
        android:layout_weight="8"/>

</LinearLayout>