Skip to content

Commit 3375f54

Browse files
authored
Merge pull request #402 from hossain-khan/copilot/improve-accessibility-support
Add accessibility support with content descriptions and semantic labels
2 parents d9f12fe + 0d00844 commit 3375f54

File tree

9 files changed

+283
-23
lines changed

9 files changed

+283
-23
lines changed

ACCESSIBILITY_TESTING_CHECKLIST.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Accessibility Testing Checklist
2+
3+
This document provides a comprehensive checklist for testing the accessibility features of the Android Remote Notify app.
4+
5+
## Prerequisites
6+
7+
Before starting accessibility testing, ensure you have:
8+
- [ ] A physical Android device or emulator running Android 11 (API 30) or higher
9+
- [ ] TalkBack screen reader enabled on the device
10+
- [ ] Accessibility Scanner app installed (optional but recommended)
11+
- [ ] Font size set to various levels (100%, 150%, 200%) for testing
12+
13+
## TalkBack Navigation Testing
14+
15+
### Main Screens
16+
17+
#### Alerts List Screen
18+
- [ ] Navigate to Alerts List Screen using TalkBack
19+
- [ ] Verify "About App" button is announced correctly
20+
- [ ] Verify "Settings" button is announced correctly
21+
- [ ] Verify "Add Alert" floating action button is announced clearly
22+
- [ ] Verify battery level card announces "Battery level at X percent"
23+
- [ ] Verify storage card announces "Storage available X of Y gigabytes"
24+
- [ ] Verify device status card is properly merged and readable
25+
- [ ] Test navigation through empty alerts state
26+
- [ ] Test navigation through configured alerts list
27+
- [ ] Verify each alert item is selectable and announces alert type
28+
- [ ] Verify delete button announces correct alert type being deleted
29+
- [ ] Test "Learn More" button in empty state
30+
31+
#### Notification Medium List Screen
32+
- [ ] Navigate to screen and verify title is read
33+
- [ ] Verify back button announces "Navigate back"
34+
- [ ] Navigate through each notification medium card
35+
- [ ] Verify each medium's icon and name is properly announced
36+
- [ ] Verify configuration status (Configured/Not Configured) is read
37+
- [ ] Test edit/configure button announces medium name
38+
- [ ] Test reset button announces medium name when visible
39+
- [ ] Navigate to check frequency slider
40+
- [ ] Verify slider value changes are announced
41+
- [ ] Test feedback section is accessible
42+
43+
#### Add/Edit Alert Screen
44+
- [ ] Navigate to "Add New Alert" screen
45+
- [ ] Verify back button is announced
46+
- [ ] Navigate through alert type selector buttons
47+
- [ ] Verify battery and storage icons are identified
48+
- [ ] Navigate to threshold slider
49+
- [ ] Verify slider changes are announced with values
50+
- [ ] Test preview card is readable with merged content
51+
- [ ] Verify save/update button is clearly announced
52+
- [ ] Test battery optimization card if shown
53+
54+
#### Alert Check Log Viewer Screen
55+
- [ ] Navigate to logs screen
56+
- [ ] Verify back button announces correctly
57+
- [ ] Test filter button with and without active filters
58+
- [ ] Verify badge announces filter status
59+
- [ ] Navigate through log items
60+
- [ ] Test expandable log details
61+
- [ ] Verify date and time information is read correctly
62+
- [ ] Test clear filters button
63+
64+
## Content Descriptions Audit
65+
66+
### Icons with Content Descriptions
67+
- [ ] Battery icon: "Battery level at X percent"
68+
- [ ] Storage icon: "Storage available X of Y gigabytes"
69+
- [ ] Battery alert icon: "Battery alert icon"
70+
- [ ] Storage alert icon: "Storage alert icon"
71+
- [ ] Delete button: "Delete battery/storage alert"
72+
- [ ] Pending check icon: "Pending check"
73+
- [ ] Schedule icon: "Worker schedule"
74+
- [ ] No alerts icon: "No alerts configured"
75+
- [ ] Notification medium icons: "[Medium name] notification medium"
76+
- [ ] Settings icon: "Configure [medium name]"
77+
- [ ] Reset icon: "Reset [medium name] configuration"
78+
- [ ] Refresh icon: "Check frequency configuration"
79+
- [ ] Filter icon: "Filter logs" or "Filter logs (filters active)"
80+
- [ ] More options icon: "More options"
81+
82+
### Buttons and Interactive Elements
83+
- [ ] All IconButtons have meaningful content descriptions
84+
- [ ] All navigation buttons announce "Navigate back"
85+
- [ ] All action buttons clearly state their purpose
86+
- [ ] Segmented buttons rely on text labels (icons set to null)
87+
- [ ] Menu items with icons set icon descriptions to null (text provides context)
88+
89+
## Semantic Labels Testing
90+
91+
### Complex UI Components
92+
- [ ] Device status card merges battery and storage info into single announcement
93+
- [ ] Alert item cards have appropriate semantic structure
94+
- [ ] Notification medium cards group related information
95+
- [ ] Log items merge multiple data points appropriately
96+
97+
## Font Size Scaling
98+
99+
### Test with Different Font Sizes
100+
- [ ] 100% (default) - All text is readable
101+
- [ ] 150% - Text scales properly without clipping
102+
- [ ] 200% - UI remains functional and readable
103+
- [ ] No text truncation occurs at any scale
104+
- [ ] All interactive elements remain accessible
105+
106+
### Screens to Test at Each Size
107+
- [ ] Alerts List Screen
108+
- [ ] Notification Medium List Screen
109+
- [ ] Add/Edit Alert Screen
110+
- [ ] Alert Check Log Viewer Screen
111+
- [ ] Configuration screens for each medium
112+
113+
## Touch Target Sizes
114+
115+
### Minimum Touch Target Verification (48dp × 48dp)
116+
- [ ] All IconButtons meet minimum size
117+
- [ ] Floating Action Button is appropriately sized
118+
- [ ] List item touch targets are sufficient
119+
- [ ] Slider thumb is large enough to manipulate
120+
- [ ] Filter chips are appropriately sized
121+
- [ ] All buttons meet minimum touch target
122+
123+
## Contrast Ratios (WCAG AA)
124+
125+
### Visual Verification
126+
- [ ] Primary text has 4.5:1 contrast ratio
127+
- [ ] Secondary text has 4.5:1 contrast ratio
128+
- [ ] Error text (low battery indicator) is distinguishable
129+
- [ ] Icon colors meet contrast requirements
130+
- [ ] Button text is readable in both light and dark modes
131+
- [ ] Disabled state has appropriate visual feedback
132+
133+
### Test Both Themes
134+
- [ ] Light theme passes all contrast checks
135+
- [ ] Dark theme passes all contrast checks
136+
137+
## Focus Order
138+
139+
### Navigation Flow Testing
140+
- [ ] Logical reading order on Alerts List Screen (top to bottom)
141+
- [ ] Settings and configuration screens follow natural flow
142+
- [ ] Form fields in Add Alert screen follow logical order
143+
- [ ] Filter options are in sensible order
144+
- [ ] No focus traps or inaccessible areas
145+
- [ ] Tab navigation (if using keyboard) follows visual order
146+
147+
## Accessibility Scanner Results
148+
149+
### Run Accessibility Scanner on Each Screen
150+
- [ ] Alerts List Screen - No critical issues
151+
- [ ] Notification Medium List Screen - No critical issues
152+
- [ ] Add/Edit Alert Screen - No critical issues
153+
- [ ] Alert Check Log Viewer Screen - No critical issues
154+
- [ ] Configuration screens - No critical issues
155+
156+
### Address Scanner Recommendations
157+
- [ ] Review and address any content description suggestions
158+
- [ ] Review and address any touch target size warnings
159+
- [ ] Review and address any contrast ratio warnings
160+
- [ ] Review and address any text sizing suggestions
161+
162+
## High Contrast Mode
163+
164+
### Test with System High Contrast Enabled
165+
- [ ] All UI elements remain visible
166+
- [ ] Text is clearly readable
167+
- [ ] Icons are distinguishable
168+
- [ ] Interactive elements are identifiable
169+
- [ ] No information is lost in high contrast mode
170+
171+
## Voice Input Testing
172+
173+
### Test Voice Input for Text Fields
174+
- [ ] Configuration screens accept voice input for URLs/emails
175+
- [ ] Voice input works for all text fields
176+
- [ ] Voice commands can activate buttons (if supported)
177+
178+
## State Announcements
179+
180+
### Dynamic Content Updates
181+
- [ ] Slider value changes are announced
182+
- [ ] Filter application announces results
183+
- [ ] Success/error states are announced
184+
- [ ] Loading states are communicated
185+
- [ ] Empty states provide clear guidance
186+
187+
## Edge Cases
188+
189+
### Special Scenarios
190+
- [ ] App behaves correctly with TalkBack during first launch
191+
- [ ] Education/tutorial sheets are accessible
192+
- [ ] Bottom sheets can be dismissed accessibly
193+
- [ ] Dialogs announce their purpose
194+
- [ ] Snackbars are read by TalkBack
195+
- [ ] Long-press actions have alternatives
196+
197+
## Material Design Guidelines Compliance
198+
199+
### Material 3 Accessibility Features
200+
- [ ] Uses Material3 components with built-in accessibility
201+
- [ ] Typography scales properly with system settings
202+
- [ ] Touch ripples provide visual feedback
203+
- [ ] State layers indicate interactive elements
204+
- [ ] Elevation and shadows don't impact readability
205+
206+
## Testing Notes
207+
208+
### Device/Emulator Used
209+
- Device Model: _________________
210+
- Android Version: ______________
211+
- TalkBack Version: _____________
212+
- Date Tested: _________________
213+
214+
### Issues Found
215+
| Screen | Issue | Severity | Status |
216+
|--------|-------|----------|--------|
217+
| | | | |
218+
219+
### Recommendations
220+
_Add any suggestions for future accessibility improvements here_
221+
222+
---
223+
224+
## Resources
225+
226+
- [Android Accessibility Guide](https://developer.android.com/guide/topics/ui/accessibility)
227+
- [Compose Accessibility Documentation](https://developer.android.com/jetpack/compose/accessibility)
228+
- [Material Design Accessibility](https://m3.material.io/foundations/accessible-design/overview)
229+
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
230+
- [Accessibility Scanner App](https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor)
231+
232+
## Completion Status
233+
234+
- [ ] All tests completed
235+
- [ ] All critical issues resolved
236+
- [ ] Documentation updated
237+
- [ ] Team notified of results

app/src/main/java/dev/hossain/remotenotify/ui/addalert/AddNewRemoteAlert.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ fun AddNewRemoteAlertUi(
313313
title = { Text(screenTitle) },
314314
navigationIcon = {
315315
IconButton(onClick = { state.eventSink(AddNewRemoteAlertScreen.Event.NavigateBack) }) {
316-
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
316+
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Navigate back")
317317
}
318318
},
319319
)
@@ -445,7 +445,11 @@ fun AddNewRemoteAlertUi(
445445
leadingContent = {
446446
Icon(
447447
painter = painterResource(state.selectedAlertType.toIconResId()),
448-
contentDescription = null,
448+
contentDescription =
449+
when (state.selectedAlertType) {
450+
AlertType.BATTERY -> "Battery alert"
451+
AlertType.STORAGE -> "Storage alert"
452+
},
449453
)
450454
},
451455
)
@@ -523,7 +527,7 @@ private fun AlertTypeSelector(
523527
AlertType.STORAGE -> R.drawable.hard_disk_24dp
524528
},
525529
),
526-
contentDescription = null,
530+
contentDescription = null, // Button text provides context
527531
)
528532
}
529533
},

app/src/main/java/dev/hossain/remotenotify/ui/alertchecklog/AlertCheckLogViewerScreen.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ fun AlertCheckLogViewerUi(
304304
}) {
305305
Icon(
306306
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
307-
contentDescription = "Navigate Back",
307+
contentDescription = "Navigate back",
308308
)
309309
}
310310
},
@@ -320,7 +320,7 @@ fun AlertCheckLogViewerUi(
320320
) {
321321
Icon(
322322
painter = painterResource(R.drawable.filter_alt_24dp),
323-
contentDescription = "Filter",
323+
contentDescription = if (hasActiveFilters) "Filter logs (filters active)" else "Filter logs",
324324
tint =
325325
if (hasActiveFilters) {
326326
MaterialTheme.colorScheme.primary
@@ -336,7 +336,7 @@ fun AlertCheckLogViewerUi(
336336
IconButton(onClick = { showOptionsMenu = true }) {
337337
Icon(
338338
imageVector = Icons.Default.MoreVert,
339-
contentDescription = "More Options",
339+
contentDescription = "More options",
340340
)
341341
}
342342
DropdownMenu(
@@ -347,7 +347,7 @@ fun AlertCheckLogViewerUi(
347347
DropdownMenuItem(
348348
text = { Text("Export Logs") },
349349
leadingIcon = {
350-
Icon(Icons.Default.Share, contentDescription = null)
350+
Icon(Icons.Default.Share, contentDescription = null) // Menu item has text label
351351
},
352352
onClick = {
353353
showOptionsMenu = false
@@ -364,7 +364,7 @@ fun AlertCheckLogViewerUi(
364364
DropdownMenuItem(
365365
text = { Text("Clear All Filters") },
366366
leadingIcon = {
367-
Icon(Icons.Default.Clear, contentDescription = null)
367+
Icon(Icons.Default.Clear, contentDescription = null) // Menu item has text label
368368
},
369369
enabled = hasActiveFilters,
370370
onClick = {

app/src/main/java/dev/hossain/remotenotify/ui/alertlist/AlertsListScreen.kt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import androidx.compose.ui.Alignment
4444
import androidx.compose.ui.Modifier
4545
import androidx.compose.ui.platform.LocalContext
4646
import androidx.compose.ui.res.painterResource
47+
import androidx.compose.ui.semantics.contentDescription
48+
import androidx.compose.ui.semantics.semantics
4749
import androidx.compose.ui.tooling.preview.Preview
4850
import androidx.compose.ui.unit.dp
4951
import androidx.work.WorkManager
@@ -366,7 +368,12 @@ private fun DeviceCurrentStateUi(state: AlertsListScreen.State) {
366368
Card(
367369
modifier =
368370
Modifier
369-
.fillMaxWidth(),
371+
.fillMaxWidth()
372+
.semantics(mergeDescendants = true) {
373+
contentDescription =
374+
"Device status: Battery at ${state.batteryPercentage} percent, " +
375+
"Storage ${state.availableStorage} of ${state.totalStorage} gigabytes available"
376+
},
370377
colors =
371378
CardDefaults.cardColors(
372379
containerColor = MaterialTheme.colorScheme.surface,
@@ -383,7 +390,7 @@ private fun DeviceCurrentStateUi(state: AlertsListScreen.State) {
383390
) {
384391
Icon(
385392
painter = painterResource(id = R.drawable.battery_5_bar_24dp),
386-
contentDescription = "Battery Status",
393+
contentDescription = "Battery level at ${state.batteryPercentage} percent",
387394
tint =
388395
if (state.batteryPercentage > 20) {
389396
MaterialTheme.colorScheme.primary
@@ -431,7 +438,7 @@ private fun DeviceCurrentStateUi(state: AlertsListScreen.State) {
431438
) {
432439
Icon(
433440
painter = painterResource(id = R.drawable.hard_disk_24dp),
434-
contentDescription = "Storage Status",
441+
contentDescription = "Storage available ${state.availableStorage} of ${state.totalStorage} gigabytes",
435442
tint = MaterialTheme.colorScheme.primary,
436443
)
437444
Spacer(modifier = Modifier.width(8.dp))
@@ -475,7 +482,11 @@ fun NotificationItem(
475482
is RemoteAlert.StorageAlert ->
476483
painterResource(id = R.drawable.hard_disk_24dp)
477484
},
478-
contentDescription = null,
485+
contentDescription =
486+
when (remoteAlert) {
487+
is RemoteAlert.BatteryAlert -> "Battery alert icon"
488+
is RemoteAlert.StorageAlert -> "Storage alert icon"
489+
},
479490
modifier = Modifier.size(36.dp),
480491
)
481492
},
@@ -524,7 +535,11 @@ fun NotificationItem(
524535
) {
525536
Icon(
526537
imageVector = Icons.Default.Delete,
527-
contentDescription = "Delete Alert",
538+
contentDescription =
539+
when (remoteAlert) {
540+
is RemoteAlert.BatteryAlert -> "Delete battery alert"
541+
is RemoteAlert.StorageAlert -> "Delete storage alert"
542+
},
528543
modifier = Modifier.size(24.dp),
529544
)
530545
}

app/src/main/java/dev/hossain/remotenotify/ui/alertlist/EmptyNotificationsStateUi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal fun EmptyNotificationsState(onLearnMoreClick: () -> Unit) {
3333
) {
3434
Icon(
3535
Icons.Default.Notifications,
36-
contentDescription = null,
36+
contentDescription = "No alerts configured",
3737
modifier =
3838
Modifier
3939
.size(72.dp)

0 commit comments

Comments
 (0)