Skip to content

Commit

Permalink
Improve accessibility in the demo app
Browse files Browse the repository at this point in the history
  • Loading branch information
MGaetan89 committed Dec 5, 2024
1 parent bd654de commit 55788ec
Show file tree
Hide file tree
Showing 26 changed files with 761 additions and 248 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSo
}
}

@Suppress("UndocumentedPublicClass")
@Suppress("StringLiteralDuplication", "UndocumentedPublicClass")
companion object {
private val URL = DemoItem.AppleBasic_16_9_TS_HLS.uri
private val videoDuration = 1800.05.seconds
Expand All @@ -94,6 +94,7 @@ class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSo
title = "Starts and ends with a blocked time range",
uri = ID_START_END,
description = "Blocked times ranges at 00:00 - 00:10 and 25:00 - 30:00",
languageTag = "en-CH",
)

/**
Expand All @@ -102,7 +103,8 @@ class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSo
val DemoItemBlockedTimeRangeOverlaps = DemoItem.URL(
title = "Blocked time ranges are overlapping",
uri = ID_OVERLAP,
description = "Blocked times ranges at 00:10 to 00:50 and 00:15 to 05:00"
description = "Blocked times ranges at 00:10 to 00:50 and 00:15 to 05:00",
languageTag = "en-CH",
)

/**
Expand All @@ -111,7 +113,8 @@ class BlockedTimeRangeAssetLoader(context: Context) : AssetLoader(DefaultMediaSo
val DemoItemBlockedTimeRangeIncluded = DemoItem.URL(
title = "Blocked time range is included in an other one",
uri = ID_INCLUDED,
description = "Blocked times ranges at 00:15 - 00:30 and 00:10 - 01:00"
description = "Blocked times ranges at 00:15 - 00:30 and 00:10 - 01:00",
languageTag = "en-CH",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ExamplesViewModel(application: Application) : AndroidViewModel(application
urn = item.urn,
description = "DRM-protected video",
imageUri = item.imageUrl.rawUrl,
languageTag = "fr-CH",
)
}
val listTokenProtectedContent = repository.getTvLiveCenter(Bu.RTS, PROTECTED_CONTENT_PAGE_SIZE).getOrDefault(emptyList())
Expand All @@ -51,6 +52,7 @@ class ExamplesViewModel(application: Application) : AndroidViewModel(application
urn = item.urn,
description = "Token-protected video",
imageUri = item.imageUrl.rawUrl,
languageTag = "fr-CH",
)
}
val allProtectedContent = listDrmContent + listTokenProtectedContent
Expand All @@ -60,7 +62,8 @@ class ExamplesViewModel(application: Application) : AndroidViewModel(application
} else {
val protectedPlaylist = Playlist(
title = "Protected streams (URNs)",
items = allProtectedContent
items = allProtectedContent,
languageTag = "en-CH",
)
val updatedPlaylists = Playlist.examplesPlaylists.toMutableList()
.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
package ch.srgssr.pillarbox.demo.shared.ui.integrationLayer

import ch.srg.dataProvider.integrationlayer.request.parameters.Bu
import ch.srg.dataProvider.integrationlayer.request.parameters.Bu.Companion.RSI
import ch.srg.dataProvider.integrationlayer.request.parameters.Bu.Companion.RTR
import ch.srg.dataProvider.integrationlayer.request.parameters.Bu.Companion.RTS
import ch.srg.dataProvider.integrationlayer.request.parameters.Bu.Companion.SRF
import ch.srg.dataProvider.integrationlayer.request.parameters.Bu.Companion.SWI
import kotlinx.serialization.Serializable

/**
Expand All @@ -14,6 +19,7 @@ import kotlinx.serialization.Serializable
@Suppress("UndocumentedPublicProperty", "UndocumentedPublicClass")
sealed interface ContentList {
val destinationTitle: String
val languageTag: String?

// Type-safe navigation does not yet support property-level @Serializable
// So, we use the BU name as a property, and recreate the BU on demand
Expand All @@ -24,6 +30,16 @@ sealed interface ContentList {
val bu: Bu
get() = Bu(buName)

override val languageTag: String?
get() = when (bu) {
SRF -> "de-CH"
RTS -> "fr-CH"
RTR -> "rm-CH"
SWI -> "de-CH"
RSI -> "it-CH"
else -> null
}

override val destinationTitle: String
get() = bu.name.uppercase()
}
Expand All @@ -39,15 +55,17 @@ sealed interface ContentList {
@Serializable
data class LatestMediaForTopic(
val urn: String,
val topic: String
val topic: String,
override val languageTag: String?,
) : ContentList {
override val destinationTitle = topic
}

@Serializable
data class LatestMediaForShow(
val urn: String,
val show: String
val show: String,
override val languageTag: String?,
) : ContentList {
override val destinationTitle = show
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import ch.srgssr.pillarbox.demo.shared.ui.integrationLayer.ContentList
*
* @property title The title of the section.
* @property contentList The list of elements in the section.
* @property languageTag The IETF BCP47 language tag of the title.
*/
data class ContentListSection(
val title: String,
val contentList: List<ContentList>
val contentList: List<ContentList>,
val languageTag: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ private val busWithoutSWI = listOf(Bu.RSI, Bu.RTR, Bu.RTS, Bu.SRF)
/**
* All the sections available in the "Lists" tab.
*/
@Suppress("StringLiteralDuplication")
val contentListSections = listOf(
ContentListSection("TV Topics", bus.map { ContentList.TVTopics(it) }),
ContentListSection("TV Shows", bus.map { ContentList.TVShows(it) }),
ContentListSection("TV Latest Videos", bus.map { ContentList.TVLatestMedias(it) }),
ContentListSection("TV Livestreams", busWithoutSWI.map { ContentList.TVLivestreams(it) }),
ContentListSection("TV Live Center", busWithoutSWI.map { ContentList.TVLiveCenter(it) }),
ContentListSection("TV Live Web", busWithoutSWI.map { ContentList.TVLiveWeb(it) }),
ContentListSection("Radio Livestreams", busWithoutSWI.map { ContentList.RadioLiveStreams(it) }),
ContentListSection("Radio Latest Audios", busWithoutSWI.map { ContentList.RadioLatestMedias(it) }),
ContentListSection("Radio Shows", busWithoutSWI.map { ContentList.RadioShows(it) }),
ContentListSection("TV Topics", bus.map { ContentList.TVTopics(it) }, languageTag = "en-CH"),
ContentListSection("TV Shows", bus.map { ContentList.TVShows(it) }, languageTag = "en-CH"),
ContentListSection("TV Latest Videos", bus.map { ContentList.TVLatestMedias(it) }, languageTag = "en-CH"),
ContentListSection("TV Livestreams", busWithoutSWI.map { ContentList.TVLivestreams(it) }, languageTag = "en-CH"),
ContentListSection("TV Live Center", busWithoutSWI.map { ContentList.TVLiveCenter(it) }, languageTag = "en-CH"),
ContentListSection("TV Live Web", busWithoutSWI.map { ContentList.TVLiveWeb(it) }, languageTag = "en-CH"),
ContentListSection("Radio Livestreams", busWithoutSWI.map { ContentList.RadioLiveStreams(it) }, languageTag = "en-CH"),
ContentListSection("Radio Latest Audios", busWithoutSWI.map { ContentList.RadioLatestMedias(it) }, languageTag = "en-CH"),
ContentListSection("Radio Shows", busWithoutSWI.map { ContentList.RadioShows(it) }, languageTag = "en-CH"),
)
1 change: 1 addition & 0 deletions pillarbox-demo-shared/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<string name="examples">Examples</string>
<string name="lists">Lists</string>
<string name="search">Search</string>
<string name="change_bu">Change BU</string>
<string name="showcases">Showcases</string>
<string name="search_placeholder">Search for content</string>
<string name="no_results">No results</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CollectionInfo
import androidx.compose.ui.semantics.CollectionItemInfo
import androidx.compose.ui.semantics.collectionInfo
import androidx.compose.ui.semantics.collectionItemInfo
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -191,6 +196,9 @@ private fun ListsMenu(
currentLocation: IlLocation?,
onServerSelected: (server: URL, forceSAM: Boolean, location: IlLocation?) -> Unit
) {
val context = LocalContext.current
val servers = remember { getServers(context).groupBy { it.serverName }.values }

var isMenuVisible by remember { mutableStateOf(false) }

IconButton(onClick = { isMenuVisible = true }) {
Expand All @@ -203,17 +211,23 @@ private fun ListsMenu(
DropdownMenu(
expanded = isMenuVisible,
onDismissRequest = { isMenuVisible = false },
modifier = Modifier.semantics {
collectionInfo = CollectionInfo(
rowCount = servers.fold(0) { value, servers ->
value + servers.size
},
columnCount = 1,
)
},
offset = DpOffset(
x = MaterialTheme.paddings.small,
y = 0.dp,
),
) {
val context = LocalContext.current
val currentServerUrl = currentServer.toString()
val servers = remember { getServers(context).groupBy { it.serverName }.values }

servers.forEachIndexed { index, environmentConfig ->
environmentConfig.forEach { config ->
environmentConfig.forEachIndexed { envIndex, config ->
val isSelected = currentServerUrl == config.host.toString() &&
currentForceSAM == config.forceSAM &&
currentLocation == config.location
Expand All @@ -224,6 +238,14 @@ private fun ListsMenu(
onServerSelected(config.host, config.forceSAM, config.location)
isMenuVisible = false
},
modifier = Modifier.semantics {
collectionItemInfo = CollectionItemInfo(
rowIndex = (index * servers.size) + envIndex,
rowSpan = 1,
columnIndex = 1,
columnSpan = 1,
)
},
trailingIcon = if (isSelected) {
{
Icon(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,43 @@ import kotlin.time.Duration.Companion.seconds
*
* @param content The content to display.
* @param modifier The [Modifier] to apply to the component.
* @param languageTag The IETF BCP47 language tag of the content.
* @param onClick The action to perform when clicking the component.
*/
@Composable
fun ContentView(
content: Content,
modifier: Modifier = Modifier,
languageTag: String? = null,
onClick: () -> Unit
) {
when (content) {
is Content.Topic -> DemoListItemView(
title = content.title,
modifier = modifier.fillMaxWidth(),
languageTag = languageTag,
onClick = onClick
)

is Content.Show -> DemoListItemView(
title = content.title,
modifier = modifier.fillMaxWidth(),
languageTag = languageTag,
onClick = onClick
)

is Content.Media -> MediaView(
content = content,
modifier = modifier.fillMaxWidth(),
languageTag = languageTag,
onClick = onClick
)

is Content.Channel -> DemoListItemView(
title = content.title,
modifier = modifier.fillMaxWidth(),
subtitle = content.description,
languageTag = languageTag,
onClick = onClick
)
}
Expand All @@ -69,6 +75,7 @@ fun ContentView(
private fun MediaView(
content: Content.Media,
modifier: Modifier = Modifier,
languageTag: String? = null,
onClick: () -> Unit
) {
val mediaTypeIcon = when (content.mediaType) {
Expand All @@ -86,6 +93,7 @@ private fun MediaView(
title = content.title,
modifier = modifier,
subtitle = "$mediaTypeIcon $subtitlePrefix ${content.date} - $duration",
languageTag = languageTag,
onClick = onClick
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
import ch.srgssr.pillarbox.demo.ui.theme.paddings
Expand All @@ -18,14 +23,22 @@ import ch.srgssr.pillarbox.demo.ui.theme.paddings
*
* @param title The title of the header.
* @param modifier The [Modifier] of the layout.
* @param languageTag The IETF BCP47 language tag of the title.
*/
@Composable
fun DemoListHeaderView(
title: String,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
languageTag: String? = null,
) {
val localeList = languageTag?.let { LocaleList(Locale(languageTag)) }

Text(
text = title,
text = buildAnnotatedString {
withStyle(SpanStyle(localeList = localeList)) {
append(title)
}
},
modifier = modifier.padding(
top = MaterialTheme.paddings.baseline,
bottom = MaterialTheme.paddings.small
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
import ch.srgssr.pillarbox.demo.ui.theme.paddings
Expand All @@ -32,13 +37,15 @@ import ch.srgssr.pillarbox.demo.ui.theme.paddings
* @param title The title of the item.
* @param modifier The [Modifier] to apply to the root of the item.
* @param subtitle The optional subtitle of the item.
* @param languageTag The IETF BCP47 language tag of the title and subtitle.
* @param onClick The action to perform when an item is clicked.
*/
@Composable
fun DemoListItemView(
title: String,
modifier: Modifier = Modifier,
subtitle: String? = null,
languageTag: String? = null,
onClick: () -> Unit,
) {
Column(
Expand All @@ -50,16 +57,26 @@ fun DemoListItemView(
vertical = MaterialTheme.paddings.small
)
) {
val localeList = languageTag?.let { LocaleList(Locale(languageTag)) }

Text(
text = title,
text = buildAnnotatedString {
withStyle(SpanStyle(localeList = localeList)) {
append(title)
}
},
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.bodyMedium
)

if (!subtitle.isNullOrBlank()) {
Text(
text = subtitle,
text = buildAnnotatedString {
withStyle(SpanStyle(localeList = localeList)) {
append(subtitle)
}
},
modifier = Modifier.padding(top = MaterialTheme.paddings.micro),
color = MaterialTheme.colorScheme.outline,
overflow = TextOverflow.Ellipsis,
Expand Down
Loading

0 comments on commit 55788ec

Please sign in to comment.