Skip to content
Snippets Groups Projects
Commit da7234be authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "[Spa] Add title contentDescription for Preference" into main

parents 1698cd11 239aa9d9
No related branches found
No related tags found
No related merge requests found
Showing with 74 additions and 16 deletions
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.spa.framework.compose
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
/** Sets the content description of this node. */
fun Modifier.contentDescription(contentDescription: String?) =
if (contentDescription != null) this.semantics {
this.contentDescription = contentDescription
} else this
......@@ -48,10 +48,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
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.settingslib.spa.debug.UiModePreviews
import com.android.settingslib.spa.framework.compose.contentDescription
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraSmall
......@@ -191,8 +190,7 @@ private fun Buttons(buttons: List<CardButton>, color: Color) {
private fun Button(button: CardButton, color: Color) {
TextButton(
onClick = button.onClick,
modifier =
Modifier.semantics { button.contentDescription?.let { this.contentDescription = it } }
modifier = Modifier.contentDescription(button.contentDescription),
) {
Text(text = button.text, color = color)
}
......
......@@ -28,6 +28,7 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
......@@ -41,7 +42,8 @@ internal fun BaseLayout(
title: String,
subTitle: @Composable () -> Unit,
modifier: Modifier = Modifier,
icon: (@Composable () -> Unit)? = null,
titleContentDescription: String? = null,
icon: @Composable (() -> Unit)? = null,
enabled: () -> Boolean = { true },
paddingStart: Dp = SettingsDimension.itemPaddingStart,
paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
......@@ -51,6 +53,7 @@ internal fun BaseLayout(
Row(
modifier = modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {}
.padding(end = paddingEnd),
verticalAlignment = Alignment.CenterVertically,
) {
......@@ -58,6 +61,7 @@ internal fun BaseLayout(
BaseIcon(icon, alphaModifier, paddingStart)
Titles(
title = title,
titleContentDescription = titleContentDescription,
subTitle = subTitle,
modifier = alphaModifier
.weight(1f)
......@@ -87,9 +91,14 @@ internal fun BaseIcon(
// Extracts a scope to avoid frequent recompose outside scope.
@Composable
private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) {
private fun Titles(
title: String,
titleContentDescription: String?,
subTitle: @Composable () -> Unit,
modifier: Modifier,
) {
Column(modifier) {
SettingsTitle(title)
SettingsTitle(title, titleContentDescription)
subTitle()
}
}
......
......@@ -32,6 +32,8 @@ internal fun BasePreference(
title: String,
summary: () -> String,
modifier: Modifier = Modifier,
titleContentDescription: String? = null,
summaryContentDescription: () -> String? = { null },
singleLineSummary: Boolean = false,
icon: @Composable (() -> Unit)? = null,
enabled: () -> Boolean = { true },
......@@ -42,9 +44,11 @@ internal fun BasePreference(
) {
BaseLayout(
title = title,
titleContentDescription = titleContentDescription,
subTitle = {
SettingsBody(
body = summary(),
contentDescription = summaryContentDescription(),
maxLines = if (singleLineSummary) 1 else Int.MAX_VALUE,
)
},
......
......@@ -63,12 +63,24 @@ interface PreferenceModel {
*/
val title: String
/**
* The content description of [title].
*/
val titleContentDescription: String?
get() = null
/**
* The summary of this [Preference].
*/
val summary: () -> String
get() = { "" }
/**
* The content description of [summary].
*/
val summaryContentDescription: () -> String?
get() = { null }
/**
* The icon of this [Preference].
*
......@@ -112,7 +124,9 @@ fun Preference(
EntryHighlight {
BasePreference(
title = model.title,
titleContentDescription = model.titleContentDescription,
summary = model.summary,
summaryContentDescription = model.summaryContentDescription,
singleLineSummary = singleLineSummary,
modifier = modifier,
icon = model.icon,
......
......@@ -21,8 +21,7 @@ import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import com.android.settingslib.spa.framework.compose.contentDescription
import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
@Composable
......@@ -37,9 +36,7 @@ internal fun SettingsSwitch(
Switch(
checked = checked,
onCheckedChange = wrapOnSwitchWithLog(onCheckedChange),
modifier = if (contentDescription != null) Modifier.semantics {
this.contentDescription = contentDescription
} else Modifier,
modifier = Modifier.contentDescription(contentDescription),
enabled = changeable(),
interactionSource = interactionSource,
)
......
......@@ -30,16 +30,23 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.contentDescription
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.toMediumWeight
@Composable
fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
fun SettingsTitle(
title: String,
contentDescription: String? = null,
useMediumWeight: Boolean = false,
) {
Text(
text = title,
modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
modifier = Modifier
.padding(vertical = SettingsDimension.paddingTiny)
.contentDescription(contentDescription),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium.withWeight(useMediumWeight),
)
......@@ -81,11 +88,13 @@ fun SettingsListItem(text: String, enabled: Boolean = true) {
@Composable
fun SettingsBody(
body: String,
contentDescription: String? = null,
maxLines: Int = Int.MAX_VALUE,
) {
if (body.isNotEmpty()) {
Text(
text = body,
modifier = Modifier.contentDescription(contentDescription),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis,
......
......@@ -29,6 +29,7 @@ import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
......@@ -93,11 +94,10 @@ class PreferenceTest {
}
}
val summaryNode = composeTestRule.onNodeWithText(LONG_SUMMARY)
try {
// There is no assertHeightIsAtMost, so use the assertHeightIsAtLeast and catch the
// expected exception.
summaryNode.assertHeightIsAtLeast(lineHeightDp.times(2))
composeTestRule.onRoot().assertHeightIsAtLeast(lineHeightDp.times(5))
} catch (e: AssertionError) {
assertThat(e).hasMessageThat().contains("height")
return
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment