fix(log): a screen reader could not delete a contact at all
A Material 3 conformance audit measured three real defects on the logging screen. Deleting was reachable only by dragging. SwipeDeleteRow declared no semantics, so TalkBack saw a row of text with no actions - a switch or Voice Access user could not delete a record, not with difficulty but at all. The arming threshold was 75% of row width, roughly 249dp of continuous travel on a 360dp phone, against 120dp in this project's own SwipeableItem. Delete and undo are now custom accessibility actions on the row, which is the case the Compose accessibility guide names explicitly: swipe gestures should be exposed this way because they are hard or impossible for users with motor impairments. The undo affordance was a 29dp target with a five-second countdown running behind it - the worst place in the screen to be hard to hit, because a miss is unrecoverable. Now 48dp by 64dp, matching the mode and time rows. The trash glyph was the emoji U+1F5D1, which renders differently on every device and font and which this project forbids as an icon. ic_delete.xml already existed and is used in three other screens. Not addressed, and worth recording from the same audit: the table grid line at 0xFF3A3A3A computes to 1.65:1 against its background where Material asks 3:1, so the grid the Log page is built around is nearly invisible and gone in sunlight; and under the app's night filter the green upload tick collapses to 1.27:1 while being encoded in colour alone.
This commit is contained in:
1 parent
9576607fc6
commit
91263674a9
3 files changed
+50
-7
No files matched your search
@@ -318,6 +318,7 @@
|
||||
<string name="prefs_wavelog_qrz_label">Cookie</string>
|
||||
<string name="wavelog_copy">复制</string>
|
||||
<string name="wavelog_delete_undo">撤销</string>
|
||||
<string name="wavelog_delete_action">删除这条联络</string>
|
||||
<string name="wavelog_col_time">时间</string>
|
||||
<string name="wavelog_col_freq">频率</string>
|
||||
<string name="wavelog_col_sat">卫星</string>
|
||||
|
||||
@@ -349,6 +349,7 @@
|
||||
<string name="prefs_wavelog_qrz_label">Cookie</string>
|
||||
<string name="wavelog_copy">Copy</string>
|
||||
<string name="wavelog_delete_undo">Undo</string>
|
||||
<string name="wavelog_delete_action">Delete this contact</string>
|
||||
<string name="wavelog_col_time">Time</string>
|
||||
<string name="wavelog_col_freq">Freq</string>
|
||||
<string name="wavelog_col_sat">Satellite</string>
|
||||
|
||||
@@ -18,6 +18,9 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
|
||||
import com.rtbishop.look4sat.core.presentation.LocalSpacing
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -59,6 +62,9 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.CustomAccessibilityAction
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.customActions
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -574,10 +580,33 @@ internal fun SwipeDeleteRow(
|
||||
}
|
||||
}
|
||||
|
||||
val deleteActionLabel = stringResource(id = R.string.wavelog_delete_action)
|
||||
val undoActionLabel = stringResource(id = R.string.wavelog_delete_undo)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onSizeChanged { rowWidth = it.width }
|
||||
// Deleting was reachable ONLY by dragging, so TalkBack, switch access and Voice Access
|
||||
// users could not delete a record at all - not with difficulty, at all. The Compose
|
||||
// accessibility guide names this exact case: swipe gestures should be exposed as custom
|
||||
// actions because they are hard or impossible for users with motor impairments.
|
||||
.semantics {
|
||||
customActions = buildList {
|
||||
if (pending) {
|
||||
add(CustomAccessibilityAction(undoActionLabel) {
|
||||
pending = false
|
||||
offsetX = 0f
|
||||
true
|
||||
})
|
||||
} else {
|
||||
add(CustomAccessibilityAction(deleteActionLabel) {
|
||||
pending = true
|
||||
offsetX = -threshold
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
// Background layer (right-side icons: trash / undo + countdown)
|
||||
Box(
|
||||
@@ -603,22 +632,34 @@ internal fun SwipeDeleteRow(
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = WaveLogYellow,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(WaveLogYellow.copy(alpha = 0.15f))
|
||||
.clickable {
|
||||
// 48dp, the Material minimum. This was a 29dp target with a five-second
|
||||
// countdown running behind it, so a miss was unrecoverable - the worst
|
||||
// possible place in the screen to be hard to hit.
|
||||
.heightIn(min = 48.dp)
|
||||
.widthIn(min = 64.dp)
|
||||
.clickable(role = Role.Button) {
|
||||
pending = false
|
||||
offsetX = 0f
|
||||
}
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp)
|
||||
.wrapContentHeight(Alignment.CenterVertically)
|
||||
.padding(horizontal = 10.dp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Trash icon (always visible while swiping, yellow)
|
||||
Text(
|
||||
text = "🗑",
|
||||
fontSize = 20.sp,
|
||||
modifier = Modifier.padding(end = 16.dp)
|
||||
// A real icon, not an emoji: emoji render differently on every device and font, and
|
||||
// this project forbids them as icons. contentDescription is null because the row
|
||||
// already exposes delete as a custom accessibility action.
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_delete),
|
||||
contentDescription = null,
|
||||
tint = WaveLogYellow,
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user