Top Banner
Accessibility in Action @KellyShuster Technical Solutions to the Accessibility Challenge
77
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Android Accessibility - DroidCon Berlin 2015

Accessibility in Action

@KellyShuster

Technical Solutions to the Accessibility Challenge

Page 2: Android Accessibility - DroidCon Berlin 2015

ScheduleWhat is Accessibility?

Android vs iOS

Exploring Android Accessibility Features

Coding Accessible Apps

Page 3: Android Accessibility - DroidCon Berlin 2015
Page 4: Android Accessibility - DroidCon Berlin 2015
Page 5: Android Accessibility - DroidCon Berlin 2015
Page 6: Android Accessibility - DroidCon Berlin 2015
Page 7: Android Accessibility - DroidCon Berlin 2015

What is Accessibility?

Page 8: Android Accessibility - DroidCon Berlin 2015

Mobility Impairment

Audio Impairment

Vision Impairment

Page 9: Android Accessibility - DroidCon Berlin 2015

Mobility Impairment

Dexterity

Issues Complete

Paralysis

Page 10: Android Accessibility - DroidCon Berlin 2015

Mobility Impairment

Dexterity

Issues Complete

Paralysis

48 dp Rhythm

Custom touch & hold delay

Head switch

Sip & puff

EMG sensors

Page 11: Android Accessibility - DroidCon Berlin 2015

Audio Impairment

Hearing

Loss Complete

Deafness

Page 12: Android Accessibility - DroidCon Berlin 2015

Audio Impairment

Hearing

Loss Complete

Deafness

Audio notifications must have visual component

System wide closed captioning

Page 13: Android Accessibility - DroidCon Berlin 2015

Visual Impairment

Color

Blindness

Complete

Blindness

Low

Vision

Page 14: Android Accessibility - DroidCon Berlin 2015

Visual Impairment

Color

Blindness

Complete

Blindness

Color Settings TalkBack

Low

Vision

Contrast Settings

Page 15: Android Accessibility - DroidCon Berlin 2015

Android vs iOS

Page 16: Android Accessibility - DroidCon Berlin 2015

TalkBack (Donut) VoiceOver (iOS 3)

Page 17: Android Accessibility - DroidCon Berlin 2015

Poor Documentation

Smaller Community

Good Documentation

Larger Community

Page 18: Android Accessibility - DroidCon Berlin 2015

No Screen Blanking

No Quick On/Off

Screen blanking

Three Tap On/Off

Page 19: Android Accessibility - DroidCon Berlin 2015

OPEN! CLOSED!

Page 20: Android Accessibility - DroidCon Berlin 2015

How to Use TalkBack

Page 21: Android Accessibility - DroidCon Berlin 2015

TalkBack Basics

Touch to Explore

Page 22: Android Accessibility - DroidCon Berlin 2015

TalkBack Basics

Touch to Explore

Read through elements (swipe left or right)

Page 23: Android Accessibility - DroidCon Berlin 2015

TalkBack Basics

Touch to Explore

Read through elements (swipe left or right)

Double tap to select

Page 24: Android Accessibility - DroidCon Berlin 2015

TalkBack Basics

Touch to Explore

Read through elements (swipe left or right)

Double tap to select

Tap once to silence

Page 25: Android Accessibility - DroidCon Berlin 2015

TalkBack Basics

Touch to Explore

Read through elements (swipe left or right)

Double tap to select

Tap once to silence

Listen for “Earcon” clues

Page 26: Android Accessibility - DroidCon Berlin 2015

Special Gestures

Global Context Menu

BackHomeLocal Context Menu

Page 27: Android Accessibility - DroidCon Berlin 2015

Special Gestures

Global Context Menu

BackHomeLocal Context Menu

Page 28: Android Accessibility - DroidCon Berlin 2015

Development

Page 29: Android Accessibility - DroidCon Berlin 2015

Development Tips & TricksTalkBack on-off turns off USB debugging!

Page 30: Android Accessibility - DroidCon Berlin 2015

Development Tips & TricksTalkBack on-off turns off USB debugging!

TalkBack not supported in emulator

Page 31: Android Accessibility - DroidCon Berlin 2015

Development Tips & TricksTalkBack on-off turns off USB debugging!

TalkBack not supported in emulator

No screenshots allowed with TalkBack on

Page 32: Android Accessibility - DroidCon Berlin 2015

Development Tips & TricksTalkBack on-off turns off USB debugging!

TalkBack not supported in emulator

No screenshots allowed with TalkBack on

Work with QA

Page 33: Android Accessibility - DroidCon Berlin 2015

Demo

Page 34: Android Accessibility - DroidCon Berlin 2015

ImageView & ImageButton

Page 35: Android Accessibility - DroidCon Berlin 2015

Image with Meaning

Page 36: Android Accessibility - DroidCon Berlin 2015

Image with Meaning<ImageView android:id="@+id/image_bulbasaur_quiz" android:layout_width="75dp" android:layout_height="75dp" android:layout_below="@id/text_question" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:contentDescription="@string/bulbasaur" android:src="@drawable/bulbasaur" />

Page 37: Android Accessibility - DroidCon Berlin 2015

<ImageView android:id="@+id/image_bulbasaur_quiz" android:layout_width="75dp" android:layout_height="75dp" android:layout_below="@id/text_question" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:contentDescription="@string/bulbasaur" android:src="@drawable/bulbasaur" />

Image with Meaning

Page 38: Android Accessibility - DroidCon Berlin 2015

Decorative Image

Page 39: Android Accessibility - DroidCon Berlin 2015

Decorative Image<ImageView android:id="@+id/image_border_1" android:layout_width="120dp" android:layout_height="30dp" android:contentDescription="@null" android:src="@drawable/ballons" />

Page 40: Android Accessibility - DroidCon Berlin 2015

<ImageView android:id="@+id/image_border_1" android:layout_width="120dp" android:layout_height="30dp" android:contentDescription="@null" android:src="@drawable/ballons" />

Decorative Image

Page 41: Android Accessibility - DroidCon Berlin 2015

<ImageView android:id="@+id/image_border_1" android:layout_width="120dp" android:layout_height="30dp" android:contentDescription="@null" android:src="@drawable/ballons" />

Decorative Image

Page 42: Android Accessibility - DroidCon Berlin 2015

States

Page 43: Android Accessibility - DroidCon Berlin 2015

States: PokeballmAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

Page 44: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 45: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 46: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 47: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 48: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 49: Android Accessibility - DroidCon Berlin 2015

mAllPokemon = (LinearLayout) findViewById(R.id.layout_all_pokemon);mPokemonToggleButton = (ImageButton) findViewById(R.id.image_button_pokeball);mPokemonToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ mAllPokemon.setVisibility(View.GONE); mPokemonToggleButton.setImageResource(R.drawable.pokeball_closed); mPokemonToggleButton.setContentDescription(getString(R.string.show_pokemon)); } else { mAllPokemon.setVisibility(View.VISIBLE); ... mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); } }});

States: Pokeball

Page 50: Android Accessibility - DroidCon Berlin 2015

States: Pokemon Visibility@Overridepublic void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ ... } else { mAllPokemon.setVisibility(View.VISIBLE); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mAllPokemon.announceForAccessibility(getString(R.string.now_showing_pokemon)); } else{ mAllPokemon.setContentDescription(getString(R.string.now_showing_pokemon)); mAllPokemon.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); } mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); }}

Page 51: Android Accessibility - DroidCon Berlin 2015

States: Pokemon Visibility@Overridepublic void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ ... } else { mAllPokemon.setVisibility(View.VISIBLE); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mAllPokemon.announceForAccessibility(getString(R.string.now_showing_pokemon)); } else{ mAllPokemon.setContentDescription(getString(R.string.now_showing_pokemon)); mAllPokemon.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); } mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); }}

Page 52: Android Accessibility - DroidCon Berlin 2015

@Overridepublic void onClick(View v) { if(mAllPokemon.getVisibility() == View.VISIBLE){ ... } else { mAllPokemon.setVisibility(View.VISIBLE); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mAllPokemon.announceForAccessibility(getString(R.string.now_showing_pokemon)); } else{ mAllPokemon.setContentDescription(getString(R.string.now_showing_pokemon)); mAllPokemon.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); } mPokemonToggleButton.setImageResource(R.drawable.pokeball_open); mPokemonToggleButton.setContentDescription(getString(R.string.hide_pokemon)); }}

States: Pokemon Visibility

Page 53: Android Accessibility - DroidCon Berlin 2015

EditText

Page 54: Android Accessibility - DroidCon Berlin 2015

<EditText android:id="@+id/edit_demo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/text_view_different" android:hint="@string/type_here"/>

EditText

Page 55: Android Accessibility - DroidCon Berlin 2015

<EditText android:id="@+id/edit_demo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/text_view_different" android:hint="@string/type_here"/>

EditText

Page 56: Android Accessibility - DroidCon Berlin 2015

TextView is Different for TalkBack

Page 57: Android Accessibility - DroidCon Berlin 2015

TextView is Different for TalkBack<TextView android:id="@+id/text_view_different" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/text_view_clickable" android:layout_centerHorizontal="true" android:layout_margin="10dp" android:contentDescription="@string/monday" android:text="@string/mon" android:textSize="20sp" />

Page 58: Android Accessibility - DroidCon Berlin 2015

<TextView android:id="@+id/text_view_different" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/text_view_clickable" android:layout_centerHorizontal="true" android:layout_margin="10dp" android:contentDescription="@string/monday" android:text="@string/mon" android:textSize="20sp" />

TextView is Different for TalkBack

Page 59: Android Accessibility - DroidCon Berlin 2015

<TextView android:id="@+id/text_view_different" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/text_view_clickable" android:layout_centerHorizontal="true" android:layout_margin="10dp" android:contentDescription="@string/monday" android:text="@string/mon" android:textSize="20sp" />

TextView is Different for TalkBack

Page 60: Android Accessibility - DroidCon Berlin 2015

Toasts & Appearing Items

Page 61: Android Accessibility - DroidCon Berlin 2015

Hidden TextView@Overridepublic void onClick(View v) { if(mHiddenTextView.getVisibility() == View.VISIBLE){ mHiddenTextView.setVisibility(View.GONE); } else { mHiddenTextView.setVisibility(View.VISIBLE); mHiddenTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); }}

Page 62: Android Accessibility - DroidCon Berlin 2015

Hidden TextView@Overridepublic void onClick(View v) { if(mHiddenTextView.getVisibility() == View.VISIBLE){ mHiddenTextView.setVisibility(View.GONE); } else { mHiddenTextView.setVisibility(View.VISIBLE); mHiddenTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); }}

Hidden TextView

Page 63: Android Accessibility - DroidCon Berlin 2015

Hidden TextViewmHiddenTextView = (TextView) findViewById(R.id.text_view_hidden);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mHiddenTextView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);}

Live Regions

Page 64: Android Accessibility - DroidCon Berlin 2015

Read Layout as Element

Page 65: Android Accessibility - DroidCon Berlin 2015

Read Layout as Element// Note you must include v4 to use this.// Make sure text is read on the *layout* for VI users, not on // individual textViewsViewCompat.setImportantForAccessibility( mTextReadMe, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextAsA, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextSingle, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextItem, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);

Page 66: Android Accessibility - DroidCon Berlin 2015

// Note you must include v4 to use this.// Make sure text is read on the *layout* for VI users, not on // individual textViewsViewCompat.setImportantForAccessibility( mTextReadMe, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextAsA, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextSingle, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);ViewCompat.setImportantForAccessibility( mTextItem, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);

Read Layout as Element

Page 67: Android Accessibility - DroidCon Berlin 2015

mLayoutAllText.setContentDescription( mTextReadMe.getText().toString() + " " + mTextAsA.getText().toString() + " " + mTextSingle.getText().toString() + " " + mTextItem.getText().toString() );

Read Layout as Element

Page 68: Android Accessibility - DroidCon Berlin 2015
Page 69: Android Accessibility - DroidCon Berlin 2015
Page 70: Android Accessibility - DroidCon Berlin 2015

CardView

Page 71: Android Accessibility - DroidCon Berlin 2015

CardView CardView charmanderCard = (CardView) findViewById(R.id.card_view_charmander); charmanderCard.setContentDescription(getString(R.string.charmander_access_details)); charmanderCard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MaterialActivity.this, "Yay Charmander!", Toast.LENGTH_LONG).show(); } });

Page 72: Android Accessibility - DroidCon Berlin 2015

CardView CardView charmanderCard = (CardView) findViewById(R.id.card_view_charmander); charmanderCard.setContentDescription(getString(R.string.charmander_access_details)); charmanderCard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MaterialActivity.this, "Yay Charmander!", Toast.LENGTH_LONG).show(); } });

Page 73: Android Accessibility - DroidCon Berlin 2015

Floating Action Button

Page 74: Android Accessibility - DroidCon Berlin 2015

FAB Traversal Order@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_material);

// Set Traversal Order for Accessibility so FAB isn't hard to access FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.floating_action_button); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { fab.setAccessibilityTraversalBefore(R.id.scroll_cards); } . . .}

Page 75: Android Accessibility - DroidCon Berlin 2015

@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_material);

// Set Traversal Order for Accessibility so FAB isn't hard to access FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.floating_action_button); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { fab.setAccessibilityTraversalBefore(R.id.scroll_cards); } . . .}

FAB Traversal Order

Page 76: Android Accessibility - DroidCon Berlin 2015

@KellyShuster

KioKrofovitch/android-accessibility

Page 77: Android Accessibility - DroidCon Berlin 2015

ResourcesAndroid Accessibility Documentation https://developer.android.com/guide/topics/ui/accessibility/index.html

Kelly’s Accessibility Demo App with Comments https://github.com/KioKrofovitch/android-accessibility

The 48dp Design Rhythm http://developer.android.com/design/style/metrics-grids.html#48dp-rhythm

New Accessibility Features in Lollipop http://www.androidcentral.com/accessibility-features-android-50-lollipop

VI Opinion: iOS vs Android https://takesugar.wordpress.com/2014/07/22/accessibility-head-to-head-android-vs-apple/

Fab Library https://github.com/futuresimple/android-floating-action-button

Credits:Blind Institute of Technology http://blindinstituteoftechnology.org/

Case Study: Implementation of ADA on CU Boulder Campus, Ryan McDonald, June 2009