mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
feat(android,ios): read each library tile once, with its long press as an action
A poster's artwork carried the title's name and so did the text under it, so a screen reader said the name twice. The artwork is now left out where the text names it, and a folded franchise tile says how many more it stands for. The long press that opens a tile's menu is offered as an action, labelled on Android and as More on iOS, where tapAndHold also makes the whole tile one button. On iOS the app behind a full-screen player is hidden from VoiceOver, which walked the library behind the picture.
This commit is contained in:
1 parent
01eee78317
commit
bf3fa19c70
4 files changed
+26
-5
No files matched your search
@@ -73,6 +73,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -681,13 +684,14 @@ private fun ContinueCard(entry: Resumable, onClick: () -> Unit, onLongClick: ()
|
||||
Modifier
|
||||
.width(220.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick, onLongClickLabel = "More for this episode"),
|
||||
) {
|
||||
Box(Modifier.fillMaxWidth().aspectRatio(16f / 9f).clip(MaterialTheme.shapes.medium)) {
|
||||
// Named by the text under it; read as well, the name came twice.
|
||||
RemoteArtwork(
|
||||
entry.episode.stillUrl ?: entry.title.backdropUrl,
|
||||
entry.title.displayName,
|
||||
Modifier.fillMaxSize(),
|
||||
Modifier.fillMaxSize().clearAndSetSemantics {},
|
||||
fallback = entry.episode.thumbnailSource,
|
||||
)
|
||||
entry.episode.progress?.let { progress ->
|
||||
@@ -738,7 +742,7 @@ private fun PosterTile(
|
||||
Column(
|
||||
modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick, onLongClickLabel = "More for this title"),
|
||||
) {
|
||||
Box {
|
||||
RemoteArtwork(
|
||||
@@ -747,7 +751,8 @@ private fun PosterTile(
|
||||
(if (shared) Modifier.sharedArt(title.key) else Modifier)
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(2f / 3f)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clearAndSetSemantics {},
|
||||
fallback = title.thumbnailSource,
|
||||
)
|
||||
if (folded > 0) {
|
||||
@@ -760,7 +765,8 @@ private fun PosterTile(
|
||||
.padding(6.dp)
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.background(Color.Black.copy(alpha = 0.7f))
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp),
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
.semantics { contentDescription = "and $folded more" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,9 @@ struct RootView: View {
|
||||
.animation(.easeInOut(duration: 0.2), value: model.activity)
|
||||
}
|
||||
.background(scheme.background.ignoresSafeArea())
|
||||
// Covered by the player, but still there to VoiceOver, which
|
||||
// would otherwise walk the library behind the picture.
|
||||
.accessibilityHidden(host.isOpen && !host.minimized)
|
||||
}
|
||||
.overlay {
|
||||
// Over the shell rather than instead of it: the page behind stays
|
||||
|
||||
@@ -541,7 +541,9 @@ private struct ContinueCard: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
// Named by the text under it; read as well, the name came twice.
|
||||
RemoteArtwork(entry.episode.stillUrl ?? entry.title.backdropUrl, entry.title.displayName, fallback: entry.episode.thumbnailSource)
|
||||
.accessibilityHidden(true)
|
||||
.aspectRatio(16 / 9, contentMode: .fit)
|
||||
.overlay(alignment: .bottom) {
|
||||
if let progress = entry.episode.progress { AccentProgress(progress: progress) }
|
||||
@@ -571,6 +573,7 @@ struct PosterTile: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
RemoteArtwork(title.posterUrl ?? title.backdropUrl, title.displayName, fallback: title.thumbnailSource)
|
||||
.accessibilityHidden(true)
|
||||
.aspectRatio(2 / 3, contentMode: .fit)
|
||||
.clipShape(RoundedRectangle(cornerRadius: Corner.standard))
|
||||
.overlay(alignment: .topTrailing) {
|
||||
@@ -581,6 +584,7 @@ struct PosterTile: View {
|
||||
.padding(.horizontal, 6).padding(.vertical, 2)
|
||||
.background(.black.opacity(0.7), in: RoundedRectangle(cornerRadius: Corner.standard))
|
||||
.padding(6)
|
||||
.accessibilityLabel("and \(folded) more")
|
||||
}
|
||||
}
|
||||
Text(title.displayName).textStyle(.bodyMedium, semibold: true).foregroundStyle(scheme.onSurface)
|
||||
|
||||
@@ -115,6 +115,14 @@ private struct TapAndHold: ViewModifier {
|
||||
.animation(.easeOut(duration: 0.1), value: pressed)
|
||||
.onTapGesture(perform: onTap)
|
||||
.onLongPressGesture(perform: { onHold?() }, onPressingChanged: { pressed = $0 })
|
||||
// One element to VoiceOver, a button, with the long press as an
|
||||
// action: a gesture is not something it can perform.
|
||||
.accessibilityElement(children: .combine)
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityAction(.default, onTap)
|
||||
.accessibilityActions {
|
||||
if let onHold { Button("More", action: onHold) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user