|
| 1 | +# Accessibility |
| 2 | + |
| 3 | +Some publications declare their accessibility features and limitations as metadata which broadly mirror the [EPUB Accessibility](https://www.w3.org/TR/epub-a11y-11) specification. |
| 4 | + |
| 5 | +```kotlin |
| 6 | +val accessibility = publication.metadata.accessibility ?: Accessibility() |
| 7 | + |
| 8 | +if (accessibility.accessModesSufficient.contains(setOf(PrimaryAccessMode.TEXTUAL))) { |
| 9 | + // This publication can be read aloud with a text-to-speech engine. |
| 10 | +} |
| 11 | + |
| 12 | +if (accessibility.features.contains(Feature.DISPLAY_TRANSFORMABILITY)) { |
| 13 | + // The text and layout of this publication can be customized. |
| 14 | +} |
| 15 | +``` |
| 16 | + |
| 17 | +## Displaying accessibility metadata |
| 18 | + |
| 19 | +While the [RWPM Accessibility models](https://readium.org/webpub-manifest/contexts/default/#accessibility-metadata) provide valuable information, they may be too complex and detailed to present to the user as it is. To simplify the presentation of this metadata to users, the Readium toolkit implements the [Accessibility Metadata Display Guide](https://w3c.github.io/publ-a11y/a11y-meta-display-guide/2.0/guidelines/) specification, developed by the W3C. |
| 20 | + |
| 21 | +### How is the display guide structured? |
| 22 | + |
| 23 | +The guide contains a list of fields that can be displayed as sections in your user interface. Each field has a list of related statements. For example, the `WaysOfReading` field provides information about whether the user can customize the text and layout (`visualAdjustments`) or if it is readable with text-to-speech or dynamic braille (`nonvisualReading`). |
| 24 | + |
| 25 | +```kotlin |
| 26 | +val guide = AccessibilityMetadataDisplayGuide(publication) |
| 27 | + |
| 28 | +when (guide.waysOfReading.visualAdjustments) { |
| 29 | + VisualAdjustments.MODIFIABLE -> { |
| 30 | + // The text and layout of the publication can be customized. |
| 31 | + } |
| 32 | + VisualAdjustments.UNMODIFIABLE -> { |
| 33 | + // The text and layout cannot be modified. |
| 34 | + } |
| 35 | + VisualAdjustments.UNKNOWN -> { |
| 36 | + // No metadata provided |
| 37 | + } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +### Localized accessibility statements |
| 42 | + |
| 43 | +While you are free to manually inspect the accessibility fields, the toolkit offers an API to automatically convert them into a list of localized statements (or claims) for direct display to the user. |
| 44 | + |
| 45 | +Each statement has a *compact* and *descriptive* variant. The *descriptive* string is longer and provides more details about the claim. |
| 46 | + |
| 47 | +For example: |
| 48 | +- **Compact**: Prerecorded audio clips |
| 49 | +- **Descriptive**: Prerecorded audio clips are embedded in the content |
| 50 | + |
| 51 | +```kotlin |
| 52 | +for (statement in guide.waysOfReading.statements) { |
| 53 | + print(statement.localizedString(context, descriptive = false)) |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +If translations are missing in your language, **you are encouraged to submit a contribution [to the official W3C repository](https://github.com/w3c/publ-a11y-display-guide-localizations)**. |
| 58 | + |
| 59 | +### Displaying all the recommended fields |
| 60 | + |
| 61 | +If you wish to display all the accessibility fields as recommended in the official specification, you can iterate over all the fields and their statements in the guide. |
| 62 | + |
| 63 | +The `shouldDisplay` property indicates whether the field does not have any meaningful statement. In which case, you may skip it in your user interface. |
| 64 | + |
| 65 | +```kotlin |
| 66 | +for (field in guide.fields) { |
| 67 | + if (!field.shouldDisplay) { |
| 68 | + continue |
| 69 | + } |
| 70 | + |
| 71 | + print("Section: ${field.localizedTitle(context)}") |
| 72 | + |
| 73 | + for (statement in field.statements) { |
| 74 | + print(statement.localizedString(context, descriptive = false)) |
| 75 | + } |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +### Sample implementation in Jetpack Compose |
| 80 | + |
| 81 | +```kotlin |
| 82 | +@Composable |
| 83 | +fun AccessibilityMetadata(guide: AccessibilityMetadataDisplayGuide, modifier: Modifier = Modifier) { |
| 84 | + val context = LocalContext.current |
| 85 | + var showDescriptiveStatements: Boolean by rememberSaveable { mutableStateOf(false) } |
| 86 | + |
| 87 | + Column( |
| 88 | + modifier, |
| 89 | + verticalArrangement = Arrangement.spacedBy(16.dp) |
| 90 | + ) { |
| 91 | + Text( |
| 92 | + text = "Accessibility Claims", |
| 93 | + style = MaterialTheme.typography.titleMedium |
| 94 | + ) |
| 95 | + |
| 96 | + Row( |
| 97 | + verticalAlignment = Alignment.CenterVertically, |
| 98 | + horizontalArrangement = Arrangement.SpaceBetween, |
| 99 | + modifier = modifier |
| 100 | + .fillMaxWidth() |
| 101 | + ) { |
| 102 | + Text("Show descriptive statements") |
| 103 | + Switch( |
| 104 | + checked = showDescriptiveStatements, |
| 105 | + onCheckedChange = { showDescriptiveStatements = it } |
| 106 | + ) |
| 107 | + } |
| 108 | + |
| 109 | + for (field in guide.fields) { |
| 110 | + if (!field.shouldDisplay) { |
| 111 | + continue |
| 112 | + } |
| 113 | + |
| 114 | + Text( |
| 115 | + text = field.localizedTitle(context), |
| 116 | + style = MaterialTheme.typography.labelMedium |
| 117 | + ) |
| 118 | + |
| 119 | + for (statement in field.statements) { |
| 120 | + Text( |
| 121 | + text = statement.localizedString(context, descriptive = showDescriptiveStatements), |
| 122 | + style = MaterialTheme.typography.bodyMedium |
| 123 | + ) |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | +} |
| 128 | +``` |
0 commit comments