Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0ce183f authored by Cam Bickel's avatar Cam Bickel Committed by Wes Okuhara
Browse files

Screen capture: Add a11y labels for toolbar items

This CL updates the RadioButtonGroupItem data class to add a new
property `contentDescription`, which is used as the semantic
description. The item's label is used as a fallback semantic
description if `contentDescription` is not provided.

Bug: 430364500
Test: Manual, PreCaptureToolbarTest, PreCaptureViewModelTest,
RadioButtonGroupTest
Flag: com.android.systemui.large_screen_screencapture

Change-Id: I9a95f1290004a7151e5ed4fee35478fcd1b61682
parent 041966af
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -375,6 +375,20 @@
    <string name="screen_capture_toolbar_record_button">Record</string>
    <!-- Button text for screen sharing [CHAR LIMIT=35] -->
    <string name="screen_share_toolbar_share_button">Share</string>
    <!-- Content description for the app window screenshot button in the toolbar, when the capture mode is set to "screenshot". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_app_window_button_screenshot_a11y">Take screenshot of window</string>
    <!-- Content description for the app window screenshot button in the toolbar, when the capture mode is set to "record". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_app_window_button_record_a11y">Record window</string>
    <!-- Content description for the region button in the toolbar, when the capture mode is set to "screenshot". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_region_button_screenshot_a11y">Take screenshot of selected area</string>
    <!-- Content description for the region button in the toolbar, when the capture mode is set to "record". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_region_button_record_a11y">Record selected area</string>
    <!-- Content description for the fullscreen button in the toolbar, when the capture mode is set to "screenshot". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_fullscreen_button_screenshot_a11y">Take screenshot of entire screen</string>
    <!-- Content description for the fullscreen button in the toolbar, when the capture mode is set to "record". [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_fullscreen_button_record_a11y">Record entire screen</string>
    <!-- Content description for the settings button in the toolbar. [CHAR LIMIT=NONE] -->
    <string name="screen_capture_toolbar_settings_button_a11y">Screen capture settings</string>

    <!-- Text shown beneath partial screenshot region displaying the dimensions (width and height) of the area to be captured. [CHAR LIMIT=NONE] [SCREENSHOT=screen/7ytguTQdu3cEn4f] -->
    <string name="screen_capture_region_dimensions"><xliff:g example="210" id="width">%1$d</xliff:g> x <xliff:g example="129" id="height">%2$d</xliff:g></string>
+7 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import androidx.compose.material3.ToggleButtonColors
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import com.android.systemui.common.shared.model.Icon as IconModel
@@ -47,6 +49,7 @@ data class RadioButtonGroupItem(
    val onClick: () -> Unit,
    val icon: IconModel? = null,
    val label: String? = null,
    val contentDescription: String? = null,
)

/** A group of N icon buttons where any single icon button is selected at a time. */
@@ -73,6 +76,10 @@ fun RadioButtonGroup(
                    },
                checked = item.isSelected,
                onCheckedChange = { item.onClick() },
                modifier =
                    Modifier.semantics {
                        this.contentDescription = item.contentDescription ?: item.label ?: ""
                    },
            ) {
                if (item.icon != null && item.label != null) {
                    Icon(icon = item.icon, modifier = Modifier.size(ICON_SIZE))
+17 −1
Original line number Diff line number Diff line
@@ -24,8 +24,12 @@ import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.IconToggleButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.res.R
import com.android.systemui.screencapture.common.ui.compose.RadioButtonGroup
import com.android.systemui.screencapture.common.ui.compose.RadioButtonGroupItem
import com.android.systemui.screencapture.common.ui.compose.Toolbar
@@ -46,14 +50,22 @@ fun PreCaptureToolbar(
                icon = it.icon,
                isSelected = it.isSelected,
                onClick = it.onClick,
                contentDescription = it.contentDescription,
            )
        }

    val captureRegionButtonItems =
        viewModel.captureRegionButtonViewModels.map {
            RadioButtonGroupItem(icon = it.icon, isSelected = it.isSelected, onClick = it.onClick)
            RadioButtonGroupItem(
                icon = it.icon,
                isSelected = it.isSelected,
                onClick = it.onClick,
                contentDescription = it.contentDescription,
            )
        }

    val settingsButtonContentDescription =
        stringResource(R.string.screen_capture_toolbar_settings_button_a11y)
    Toolbar(expanded = expanded, onCloseClick = onCloseClick, modifier = modifier) {
        Row {
            if (viewModel.screenRecordingSupported) {
@@ -61,6 +73,10 @@ fun PreCaptureToolbar(
                    checked = false,
                    onCheckedChange = {},
                    shape = IconButtonDefaults.smallSquareShape,
                    modifier =
                        Modifier.semantics {
                            this.contentDescription = settingsButtonContentDescription
                        },
                ) {
                    viewModel.icons?.let { Icon(icon = it.moreOptions) }
                }
+41 −3
Original line number Diff line number Diff line
@@ -90,12 +90,19 @@ constructor(
            )

    val captureRegionButtonViewModels: List<RadioButtonGroupItemViewModel> by
        combine(captureRegionSource, iconProvider.icons) { selectedRegion, icons ->
                generateCaptureRegionButtonViewModels(selectedRegion, icons)
        combine(captureRegionSource, captureTypeSource, iconProvider.icons) {
                selectedRegion,
                selectedCaptureType,
                icons ->
                generateCaptureRegionButtonViewModels(selectedRegion, selectedCaptureType, icons)
            }
            .hydratedStateOf(
                initialValue =
                    generateCaptureRegionButtonViewModels(captureRegionSource.value, null)
                    generateCaptureRegionButtonViewModels(
                        captureRegionSource.value,
                        captureTypeSource.value,
                        null,
                    )
            )

    fun updateCaptureType(selectedType: ScreenCaptureType) {
@@ -179,6 +186,7 @@ constructor(

    private fun generateCaptureRegionButtonViewModels(
        selectedRegion: ScreenCaptureRegion,
        selectedCaptureType: ScreenCaptureType,
        icons: ScreenCaptureIcons?,
    ): List<RadioButtonGroupItemViewModel> {
        return buildList {
@@ -188,6 +196,17 @@ constructor(
                        icon = icons?.appWindow,
                        isSelected = (selectedRegion == ScreenCaptureRegion.APP_WINDOW),
                        onClick = { updateCaptureRegion(ScreenCaptureRegion.APP_WINDOW) },
                        contentDescription =
                            applicationContext.getString(
                                when (selectedCaptureType) {
                                    ScreenCaptureType.SCREENSHOT ->
                                        R.string
                                            .screen_capture_toolbar_app_window_button_screenshot_a11y
                                    ScreenCaptureType.SCREEN_RECORD ->
                                        R.string
                                            .screen_capture_toolbar_app_window_button_record_a11y
                                }
                            ),
                    )
                )
            }
@@ -197,6 +216,15 @@ constructor(
                    icon = icons?.region,
                    isSelected = (selectedRegion == ScreenCaptureRegion.PARTIAL),
                    onClick = { updateCaptureRegion(ScreenCaptureRegion.PARTIAL) },
                    contentDescription =
                        applicationContext.getString(
                            when (selectedCaptureType) {
                                ScreenCaptureType.SCREENSHOT ->
                                    R.string.screen_capture_toolbar_region_button_screenshot_a11y
                                ScreenCaptureType.SCREEN_RECORD ->
                                    R.string.screen_capture_toolbar_region_button_record_a11y
                            }
                        ),
                )
            )

@@ -205,6 +233,16 @@ constructor(
                    icon = icons?.fullscreen,
                    isSelected = (selectedRegion == ScreenCaptureRegion.FULLSCREEN),
                    onClick = { updateCaptureRegion(ScreenCaptureRegion.FULLSCREEN) },
                    contentDescription =
                        applicationContext.getString(
                            when (selectedCaptureType) {
                                ScreenCaptureType.SCREENSHOT ->
                                    R.string
                                        .screen_capture_toolbar_fullscreen_button_screenshot_a11y
                                ScreenCaptureType.SCREEN_RECORD ->
                                    R.string.screen_capture_toolbar_fullscreen_button_record_a11y
                            }
                        ),
                )
            )
        }
+3 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ constructor(
    val unselectedIcon: Icon? = null,
    val isSelected: Boolean,
    val onClick: () -> Unit,
    val contentDescription: String? = null,
) {
    init {
        require((selectedIcon != null) == (unselectedIcon != null)) {
@@ -38,12 +39,14 @@ constructor(
        icon: Icon? = null,
        isSelected: Boolean,
        onClick: () -> Unit,
        contentDescription: String? = null,
    ) : this(
        label = label,
        selectedIcon = icon,
        unselectedIcon = icon,
        isSelected = isSelected,
        onClick = onClick,
        contentDescription = contentDescription,
    )

    val icon: Icon?
Loading