Skip to content

Commit 033fa4f

Browse files
authored
Standardize settings row affordances (#2327)
- Add a shared preference row scaffold for home and settings rows - Refactor settings and head control home rows onto the shared row shell - Make time steppers compact dialog rows on phones and inline controls on tablets - Align value selector trailing affordances with stable chevron sizing and localized Done text 🤖 Auto-generated
1 parent 7f03c7b commit 033fa4f

6 files changed

Lines changed: 295 additions & 219 deletions

File tree

app/src/main/java/com/enaboapps/switchify/components/HeadControlToggleCard.kt

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,26 @@
11
package com.enaboapps.switchify.components
22

33
import androidx.compose.animation.core.animateFloatAsState
4-
import androidx.compose.foundation.background
5-
import androidx.compose.foundation.clickable
6-
import androidx.compose.foundation.layout.Arrangement
7-
import androidx.compose.foundation.layout.Box
8-
import androidx.compose.foundation.layout.Column
9-
import androidx.compose.foundation.layout.Row
10-
import androidx.compose.foundation.layout.fillMaxWidth
11-
import androidx.compose.foundation.layout.padding
12-
import androidx.compose.foundation.layout.size
13-
import androidx.compose.foundation.shape.CircleShape
14-
import androidx.compose.material3.Icon
154
import androidx.compose.material3.MaterialTheme
165
import androidx.compose.material3.Text
17-
import androidx.compose.material3.surfaceColorAtElevation
186
import androidx.compose.runtime.Composable
197
import androidx.compose.runtime.LaunchedEffect
208
import androidx.compose.runtime.getValue
219
import androidx.compose.runtime.mutableStateOf
2210
import androidx.compose.runtime.remember
2311
import androidx.compose.runtime.rememberCoroutineScope
2412
import androidx.compose.runtime.setValue
25-
import androidx.compose.ui.Alignment
2613
import androidx.compose.ui.Modifier
27-
import androidx.compose.ui.draw.clip
2814
import androidx.compose.ui.graphics.graphicsLayer
2915
import androidx.compose.ui.platform.LocalContext
3016
import androidx.compose.ui.res.painterResource
3117
import androidx.compose.ui.res.stringResource
3218
import androidx.compose.ui.text.font.FontWeight
33-
import androidx.compose.ui.unit.dp
3419
import com.enaboapps.switchify.R
3520
import com.enaboapps.switchify.service.camera.CameraPermissionManager
3621
import com.enaboapps.switchify.service.core.ServiceBridge
3722
import com.enaboapps.switchify.service.techniques.headcontrol.HeadControlSettings
3823
import com.enaboapps.switchify.service.utils.ServiceUtils
39-
import com.enaboapps.switchify.theme.Dimens
4024
import kotlinx.coroutines.delay
4125
import kotlinx.coroutines.launch
4226

@@ -84,44 +68,22 @@ fun HeadControlToggleCard(
8468

8569
val scheme = MaterialTheme.colorScheme
8670

87-
Row(
88-
modifier = modifier
89-
.fillMaxWidth()
90-
.background(scheme.surfaceColorAtElevation(1.dp))
91-
.clickable(enabled = !coolingDown, onClick = { onClick.let { it() } })
92-
.padding(Dimens.spaceM),
93-
horizontalArrangement = Arrangement.spacedBy(Dimens.spaceM),
94-
verticalAlignment = Alignment.CenterVertically
95-
) {
96-
Box(
97-
modifier = Modifier
98-
.size(40.dp)
99-
.clip(CircleShape)
100-
.background(scheme.primary.copy(alpha = 0.12f)),
101-
contentAlignment = Alignment.Center
102-
) {
103-
Icon(
71+
PreferenceRowScaffold(
72+
title = stringResource(R.string.home_head_control_title),
73+
summary = stringResource(
74+
if (headEnabled) R.string.home_head_control_on_summary else R.string.home_head_control_off_summary
75+
),
76+
modifier = modifier,
77+
enabled = !coolingDown,
78+
onClick = { onClick.let { it() } },
79+
leadingContent = {
80+
PreferenceRowLeadingIcon(
10481
painter = painterResource(id = R.drawable.ic_head_control_pointer),
10582
contentDescription = null,
106-
tint = scheme.primary,
107-
modifier = Modifier
108-
.size(22.dp)
109-
.graphicsLayer(scaleX = iconScale, scaleY = iconScale)
110-
)
111-
}
112-
Column(modifier = Modifier.weight(1f)) {
113-
Text(
114-
text = stringResource(R.string.home_head_control_title),
115-
style = MaterialTheme.typography.titleMedium
116-
)
117-
Text(
118-
text = stringResource(
119-
if (headEnabled) R.string.home_head_control_on_summary else R.string.home_head_control_off_summary
120-
),
121-
style = MaterialTheme.typography.bodySmall,
122-
color = scheme.onSurfaceVariant
83+
iconModifier = Modifier.graphicsLayer(scaleX = iconScale, scaleY = iconScale)
12384
)
12485
}
86+
) {
12587
Text(
12688
text = if (headEnabled) "ON" else "OFF",
12789
style = MaterialTheme.typography.labelLarge,

app/src/main/java/com/enaboapps/switchify/components/PreferenceComponentBase.kt

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
package com.enaboapps.switchify.components
22

3-
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.clickable
5-
import androidx.compose.foundation.layout.Arrangement
6-
import androidx.compose.foundation.layout.Box
7-
import androidx.compose.foundation.layout.Column
8-
import androidx.compose.foundation.layout.Row
9-
import androidx.compose.foundation.layout.fillMaxWidth
10-
import androidx.compose.foundation.layout.heightIn
11-
import androidx.compose.foundation.layout.padding
12-
import androidx.compose.foundation.layout.size
13-
import androidx.compose.foundation.shape.CircleShape
143
import androidx.compose.material.icons.Icons
154
import androidx.compose.material.icons.automirrored.filled.Help
165
import androidx.compose.material3.AlertDialog
@@ -19,21 +8,14 @@ import androidx.compose.material3.IconButton
198
import androidx.compose.material3.MaterialTheme
209
import androidx.compose.material3.Text
2110
import androidx.compose.material3.TextButton
22-
import androidx.compose.material3.surfaceColorAtElevation
2311
import androidx.compose.runtime.Composable
2412
import androidx.compose.runtime.getValue
2513
import androidx.compose.runtime.mutableStateOf
2614
import androidx.compose.runtime.remember
2715
import androidx.compose.runtime.setValue
28-
import androidx.compose.ui.Alignment
29-
import androidx.compose.ui.Modifier
30-
import androidx.compose.ui.draw.clip
3116
import androidx.compose.ui.graphics.vector.ImageVector
3217
import androidx.compose.ui.res.stringResource
33-
import androidx.compose.ui.text.style.TextOverflow
34-
import androidx.compose.ui.unit.dp
3518
import com.enaboapps.switchify.R
36-
import com.enaboapps.switchify.theme.Dimens
3719

3820
@Composable
3921
fun PreferenceComponentBase(
@@ -44,50 +26,16 @@ fun PreferenceComponentBase(
4426
onClick: (() -> Unit)? = null,
4527
trailing: @Composable () -> Unit
4628
) {
47-
val scheme = MaterialTheme.colorScheme
48-
val rowModifier = Modifier
49-
.fillMaxWidth()
50-
.heightIn(min = 56.dp)
51-
.background(scheme.surfaceColorAtElevation(1.dp))
52-
.let { if (onClick != null) it.clickable(onClick = onClick) else it }
53-
.padding(Dimens.spaceM)
54-
55-
Row(
56-
modifier = rowModifier,
57-
horizontalArrangement = Arrangement.spacedBy(Dimens.spaceM),
58-
verticalAlignment = Alignment.CenterVertically
59-
) {
60-
if (leadingIcon != null) {
61-
Box(
62-
modifier = Modifier
63-
.size(40.dp)
64-
.clip(CircleShape)
65-
.background(scheme.primary.copy(alpha = 0.12f)),
66-
contentAlignment = Alignment.Center
67-
) {
68-
Icon(
69-
imageVector = leadingIcon,
70-
contentDescription = null,
71-
tint = scheme.primary,
72-
modifier = Modifier.size(22.dp)
73-
)
29+
PreferenceRowScaffold(
30+
title = stringResource(titleResId),
31+
summary = stringResource(summaryResId),
32+
onClick = onClick,
33+
leadingContent = leadingIcon?.let {
34+
{
35+
PreferenceRowLeadingIcon(imageVector = it)
7436
}
7537
}
76-
Column(modifier = Modifier.weight(1f)) {
77-
Text(
78-
text = stringResource(titleResId),
79-
style = MaterialTheme.typography.titleMedium,
80-
maxLines = 2,
81-
overflow = TextOverflow.Ellipsis
82-
)
83-
Text(
84-
text = stringResource(summaryResId),
85-
style = MaterialTheme.typography.bodySmall,
86-
color = scheme.onSurfaceVariant,
87-
maxLines = 3,
88-
overflow = TextOverflow.Ellipsis
89-
)
90-
}
38+
) {
9139
if (explanationResId != null) {
9240
ExplanationButton(titleResId = titleResId, explanationResId = explanationResId)
9341
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.enaboapps.switchify.components
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.clickable
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Box
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.RowScope
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.heightIn
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.size
14+
import androidx.compose.foundation.shape.CircleShape
15+
import androidx.compose.material3.Icon
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.Text
18+
import androidx.compose.material3.surfaceColorAtElevation
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.draw.clip
23+
import androidx.compose.ui.graphics.painter.Painter
24+
import androidx.compose.ui.graphics.vector.ImageVector
25+
import androidx.compose.ui.text.style.TextOverflow
26+
import androidx.compose.ui.unit.dp
27+
import com.enaboapps.switchify.theme.Dimens
28+
29+
@Composable
30+
fun PreferenceRowScaffold(
31+
title: String,
32+
summary: String,
33+
modifier: Modifier = Modifier,
34+
enabled: Boolean = true,
35+
onClick: (() -> Unit)? = null,
36+
leadingContent: (@Composable () -> Unit)? = null,
37+
trailing: @Composable RowScope.() -> Unit
38+
) {
39+
val scheme = MaterialTheme.colorScheme
40+
val rowModifier = modifier
41+
.fillMaxWidth()
42+
.heightIn(min = 56.dp)
43+
.background(scheme.surfaceColorAtElevation(1.dp))
44+
.let { if (onClick != null) it.clickable(enabled = enabled, onClick = onClick) else it }
45+
.padding(Dimens.spaceM)
46+
47+
Row(
48+
modifier = rowModifier,
49+
horizontalArrangement = Arrangement.spacedBy(Dimens.spaceM),
50+
verticalAlignment = Alignment.CenterVertically
51+
) {
52+
leadingContent?.invoke()
53+
Column(modifier = Modifier.weight(1f)) {
54+
Text(
55+
text = title,
56+
style = MaterialTheme.typography.titleMedium,
57+
maxLines = 2,
58+
overflow = TextOverflow.Ellipsis
59+
)
60+
Text(
61+
text = summary,
62+
style = MaterialTheme.typography.bodySmall,
63+
color = scheme.onSurfaceVariant,
64+
maxLines = 3,
65+
overflow = TextOverflow.Ellipsis
66+
)
67+
}
68+
trailing()
69+
}
70+
}
71+
72+
@Composable
73+
fun PreferenceRowLeadingIcon(
74+
modifier: Modifier = Modifier,
75+
iconModifier: Modifier = Modifier,
76+
imageVector: ImageVector? = null,
77+
painter: Painter? = null,
78+
contentDescription: String? = null
79+
) {
80+
val scheme = MaterialTheme.colorScheme
81+
Box(
82+
modifier = modifier
83+
.size(40.dp)
84+
.clip(CircleShape)
85+
.background(scheme.primary.copy(alpha = 0.12f)),
86+
contentAlignment = Alignment.Center
87+
) {
88+
if (imageVector != null) {
89+
Icon(
90+
imageVector = imageVector,
91+
contentDescription = contentDescription,
92+
tint = scheme.primary,
93+
modifier = iconModifier.size(22.dp)
94+
)
95+
} else if (painter != null) {
96+
Icon(
97+
painter = painter,
98+
contentDescription = contentDescription,
99+
tint = scheme.primary,
100+
modifier = iconModifier.size(22.dp)
101+
)
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)