Home Android & Kotlin Books Android Accessibility by Tutorials

5
Perceivable — Time-Based Media & Cues Written by Victoria Gonda

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Video, audio, animation and instructions are vital parts of your app’s experience. But for those who live with certain conditions, these media types may not be useful, or even perceivable. In order to build an accessible app, you’ll need to make adjustments to your app’s design to make these kinds of media accessible to all.

In this chapter, you’ll delve deeper into the concept of perceivability, specifically how to make time-based media useful to different people. You’ll also learn best practices for giving your users cues they can use to navigate your app with the help of assistive technologies.

Displaying time-based media

As you might expect, time-based media is anything that, well, takes place over time. The obvious examples are video and audio; they start at a particular time, and then they end later. Animations also fall into this category. There is only one way to consume these media types.

That thought brings you to the prevailing WCAG guideline for this chapter:

Guideline 1.2 Time-based Media Provide alternatives for time-based media.

There are many ways you can provide these types of media. For prerecorded audio, you can have on-screen captions. If you have a video, you can include an audio track or text alternative that contains the same information. When you’re animating an instruction, you can also provide a description in audio or text form.

In some cases, you can make time-based media completely optional, allowing the user to skip it. Be careful with optional settings though — you don’t want to prevent people from accessing content that might be valuable.

Think about the guideline’s success criteria, which specify a heuristic that these elements need equivalent alternatives, for example, text, captions, or other form factors.

Consider this criterion:

Success Criterion 1.2.3 Audio Description or Media Alternative (Prerecorded): An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media, except when the media is a media alternative for text and is clearly labeled as such.

Level A

Taco Tuesday has some significant issues where time-based media is not accessible, especially in the on-boarding flow. Once again, you’ll improve the app so that you can learn.

Open up the project you used in previous chapters or use the starter project from this chapter’s materials.

Improving the on-boarding flow

Think about the many ways you could design the on-boarding process for Taco Tuesday:

Exploring the on-boarding

If you don’t see the on-boarding flow, go to Settings and select Show on-boarding. Then close and reopen the app. You can do this anytime you want to see on-boarding again.

Show on-boarding in settings.
Cjib ev-goevkoqn eb jewhoyjq.

//  val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
//  val showOnboarding = sharedPref.getBoolean("onboarding", true)
//  if (showOnboarding) {
   OnboardingActivity.startActivity(this)
//   finish()
//  }
Screenshots of on-boarding views.
Dkjiiddbujb in am-hiexkivk diemb.

Removing auto-advance

To make Taco Tuesday’s on-boarding flow more friendly, you’ll remove the auto-advance feature and add controls.

lifecycleScope.launch(Dispatchers.IO) {
 val options = resources.getStringArray(R.array.pop_up_options)
 while (isActive) {
  delay(5000) // 5 seconds

  withContext(Dispatchers.Main) {
   if (binding.onboardingPager.currentItem == NUM_PAGES - 1) {
    MainActivity.startActivity(this@OnboardingActivity)
    this.cancel()
   } else {
    binding.onboardingPager.currentItem++
   }
  }
 }
}

Adding controls

In this section, you’ll implement logic that gives your user a straightforward way to advance to the next page. First, you’ll add the layout for a Next button.

<com.google.android.material.button.MaterialButton
  android:id="@+id/onboarding_next_button"
  style="@style/Widget.MaterialComponents.Button.TextButton"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:padding="@dimen/space_normal"
  android:text="@string/onboarding_next"
  android:textColor="?colorOnPrimary"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/onboarding_next_button"
Next button.
Herd keftiv.

binding.onboardingNextButton.setOnClickListener {
 if (binding.onboardingPager.currentItem == NUM_PAGES - 1) {
  MainActivity.startActivity(this)
 } else {
  binding.onboardingPager.currentItem =
    binding.onboardingPager.currentItem + 1
 }
}
<com.google.android.material.button.MaterialButton
  android:id="@+id/onboarding_back_button"
  style="@style/Widget.MaterialComponents.Button.TextButton"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:padding="@dimen/space_normal"
  android:text="@string/onboarding_back"
  android:textColor="?colorOnPrimary"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintStart_toStartOf="parent" />
binding.onboardingBackButton.setOnClickListener {
 binding.onboardingPager.currentItem =
   binding.onboardingPager.currentItem - 1
}
binding.onboardingPager.registerOnPageChangeCallback(object :
  ViewPager2.OnPageChangeCallback() {
 override fun onPageSelected(position: Int) {
  binding.onboardingNextButton.text =
    if (position == NUM_PAGES - 1) {
     getString(R.string.onboarding_done)
    } else {
     getString(R.string.onboarding_next)
    }
  binding.onboardingBackButton.visibility =
    if (position == 0) {
     View.GONE
    } else {
     View.VISIBLE
    }
   }
 }
)
Screenshots of all button states.
Kmvaegqduwn en abf rarcef kqaxix.

Giving cues

Another important part of on-boarding is what you’re saying. How do you make sure your instructions are meaningful? For example, if you’re describing a button’s color, what does that mean for a person who doesn’t perceive color? This brings you to the second criterion you’ll explore in this chapter:

Grayscale buttons.
Vtegrwuqu tuplenr.

Improving cues in on-boarding

There are a number of things you’ll do to improve the onboarding flow’s cues. You’ll start by making the button descriptions more clear and friendly to those who rely on screen readers.

Clarifying the instructions

The instructions are defined in strings.xml, and each entry is prepended with onboarding_ for ease when searching.

Icon buttons on the detail screen.
Evub gixkimh ox tro wumauq bwfaig.

Adding text to the button

No button should be without some kind of description, so you’ll add some text to this button to make its purpose clear.

com.google.android.material.button.MaterialButton
android:src="@drawable/ic_baseline_thumb_up_24"
android:text="@string/shared_try_it"
app:icon="@drawable/ic_baseline_thumb_up_24"
Try it button.
Hzs ev hildil.

Modifying the button state

This button changes state depending on if the recipe is currently saved or not. This means you need logic to update the text and icon, depending on its state.

recipeDetailTryDiscardButton.setImageDrawable(
 ResourcesCompat.getDrawable(resources,
  R.drawable.ic_baseline_thumb_down_24,
  requireContext().theme))
recipeDetailTryDiscardButton.text =
  getString(R.string.shared_discard)
recipeDetailTryDiscardButton.icon =
  ResourcesCompat.getDrawable(resources,
    R.drawable.ic_baseline_thumb_down_24,
    requireContext().theme)
recipeDetailTryDiscardButton.setImageDrawable(
 ResourcesCompat.getDrawable(resources,
  R.drawable.ic_baseline_thumb_up_24,
  requireContext().theme))
recipeDetailTryDiscardButton.text =
  getString(R.string.shared_try_it)
recipeDetailTryDiscardButton.icon =
  ResourcesCompat.getDrawable(resources,
   R.drawable.ic_baseline_thumb_up_24,
   requireContext().theme)
Updated buttons on the detail screen.
Ojdadaf navdotv ul xcu giwioc gpleih.

Adding state-specific instructions

Now you can add instructions for this button to your on-boarding. You’ll need to add a new page just for these instructions.

<string name="onboarding_details">Manage your recipes using the \"Try it\" and \"Discard\" buttons on the detail view.</string>
<string name="onboarding_details_description">Thumbs-up icon</string>
OnboardingItem(
  R.drawable.onboarding_details,
  R.string.onboarding_details,
  R.string.onboarding_details_description
),
private val NUM_PAGES: Int
 get() = pages.size
Screenshot of new on-boarding page.
Tkzeuxzqiq eg xuy ov-vaargijm lobo.

Key points

  • Time-based media such as video, audio and animations, must be accompanied by alternatives.
  • Alternatives to visual media can be text or audio, and alternatives to audio can be equivalent visuals.
  • Users must be able to control media that’s important for them to understand. You need to provide a way for them to go back and revisit something
  • Sensory characteristics such as shape, color, size, visual location, orientation or sound should not be the only ways you give instructions.

Where to go from here?

Time-based media is a broad topic, in part, because there are many ways to use time-based media in an app.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.