Update entity button with toggle styling to match M3 components (#3923)

Update entity button styling to match M3 components
This commit is contained in:
Joris Pelgröm 2023-10-06 20:08:22 +02:00 committed by GitHub
parent fb739596fc
commit 65863148fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 75 deletions

View file

@ -4,22 +4,22 @@ import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.wear.compose.material.ToggleChip
import androidx.wear.compose.material.ToggleChipDefaults
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.LocalContentColor
import androidx.wear.compose.material3.LocalTextStyle
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.Text
import androidx.wear.tooling.preview.devices.WearDevices
import com.mikepenz.iconics.compose.Image
import io.homeassistant.companion.android.common.R
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.EntityExt
import io.homeassistant.companion.android.common.data.integration.domain
@ -28,10 +28,12 @@ import io.homeassistant.companion.android.common.data.integration.isActive
import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE
import io.homeassistant.companion.android.theme.getFilledTonalButtonColors
import io.homeassistant.companion.android.theme.wearColorScheme
import io.homeassistant.companion.android.util.ToggleSwitch
import io.homeassistant.companion.android.util.WearToggleChip
import io.homeassistant.companion.android.util.onEntityClickedFeedback
import io.homeassistant.companion.android.util.previewEntity1
import io.homeassistant.companion.android.util.previewEntity3
import io.homeassistant.companion.android.util.previewEntity4
@Composable
fun EntityUi(
@ -46,17 +48,37 @@ fun EntityUi(
val attributes = entity.attributes as Map<*, *>
val iconBitmap = entity.getIcon(LocalContext.current)
val friendlyName = attributes["friendly_name"].toString()
val nameModifier = Modifier
.fillMaxWidth()
.pointerInput(Unit) {
detectTapGestures(
onTap = {
onEntityClicked(entity.entityId, entity.state)
onEntityClickedFeedback(
isToastEnabled,
isHapticEnabled,
context,
friendlyName,
haptic
)
},
onLongPress = {
onEntityLongPressed(entity.entityId)
}
)
}
if (entity.domain in EntityExt.DOMAINS_TOGGLE) {
val isChecked = entity.isActive()
val isEnabled = entity.state != STATE_UNAVAILABLE
val colors = WearToggleChip.entityToggleChipBackgroundColors(entity, isChecked)
ToggleChip(
checked = isChecked,
onCheckedChange = {
onEntityClicked(entity.entityId, entity.state)
onEntityClickedFeedback(isToastEnabled, isHapticEnabled, context, friendlyName, haptic)
},
modifier = Modifier
.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
appIcon = {
Image(
asset = iconBitmap,
@ -64,47 +86,25 @@ fun EntityUi(
)
},
label = {
Text(
text = friendlyName,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth().pointerInput(Unit) {
detectTapGestures(
onTap = {
onEntityClicked(entity.entityId, entity.state)
onEntityClickedFeedback(
isToastEnabled,
isHapticEnabled,
context,
friendlyName,
haptic
)
},
onLongPress = {
onEntityLongPressed(entity.entityId)
}
)
}
)
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.labelMedium,
LocalContentColor provides colors.contentColor(enabled = isEnabled, checked = isChecked).value
) {
Text(
text = friendlyName,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = nameModifier
)
}
},
enabled = entity.state != STATE_UNAVAILABLE,
toggleControl = {
Icon(
imageVector = ToggleChipDefaults.switchIcon(isChecked),
contentDescription = if (isChecked) {
stringResource(R.string.enabled)
} else {
stringResource(R.string.disabled)
},
tint = if (isChecked) wearColorScheme.tertiary else wearColorScheme.onSurface
)
},
colors = WearToggleChip.entityToggleChipBackgroundColors(entity, isChecked)
enabled = isEnabled,
toggleControl = { ToggleSwitch(isChecked) },
colors = colors
)
} else {
Button(
modifier = Modifier
.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
icon = {
Image(
asset = iconBitmap,
@ -116,23 +116,7 @@ fun EntityUi(
text = friendlyName,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth().pointerInput(Unit) {
detectTapGestures(
onTap = {
onEntityClicked(entity.entityId, entity.state)
onEntityClickedFeedback(
isToastEnabled,
isHapticEnabled,
context,
friendlyName,
haptic
)
},
onLongPress = {
onEntityLongPressed(entity.entityId)
}
)
}
modifier = nameModifier
)
},
enabled = entity.state != STATE_UNAVAILABLE,
@ -163,5 +147,12 @@ private fun PreviewEntityUI() {
isToastEnabled = true,
onEntityLongPressed = { }
)
EntityUi(
entity = previewEntity4,
onEntityClicked = { _, _ -> },
isHapticEnabled = false,
isToastEnabled = true,
onEntityLongPressed = { }
)
}
}

View file

@ -19,6 +19,7 @@ val lightAttributes: Map<*, *> = mapOf(
"min_mireds" to 153,
"max_mireds" to 526,
"color_temp" to 300,
"rgb_color" to listOf(255, 187, 130),
"color_mode" to "color_temp"
)

View file

@ -27,9 +27,11 @@ import io.homeassistant.companion.android.theme.wearColorScheme
object WearToggleChip {
/**
* A function that provides chip colors that mostly follow the default toggle chip colors, but when supported
* provide a background for active entities that reflects their state (position and color). Gradient code
* is based on [androidx.wear.compose.material.ToggleChipDefaults.toggleChipColors].
* A function that provides chip colors that mostly follow M3 styling, but for use with M2
* components that can to, when supported, provide a background for active entities that
* reflects their state (position and color). M3's ToggleButton does not allow anything more
* complicated than a single color on a button. Gradient code is based on
* [androidx.wear.compose.material.ToggleChipDefaults.toggleChipColors].
*
* @param entity The entity state on which the background for the active state should be based
*/
@ -38,9 +40,9 @@ object WearToggleChip {
// For a toggleable entity, a custom background should only be used if it has:
// a. a position (eg. fan speed, light brightness)
// b. a custom color (eg. light color)
// If there is a position (a) but no color (b), use the default (theme) color for the 'active' part.
// If there is a color (b) but no position (a), use a smooth gradient similar to ToggleChip.
// If it doesn't have either or is 'off', it should use the default chip background.
// If there is a position (a) but no color (b), use the on (surfaceBright) color for the 'active' part.
// If there is a color (b) but no position (a), use the calculated color for the background.
// If it doesn't have either or is 'off', it should use the default off (surfaceDim) background.
val hasPosition = when (entity.domain) {
"cover" -> entity.state != "closed" && entity.getCoverPosition() != null
@ -53,18 +55,24 @@ object WearToggleChip {
val contentBackgroundColor = if (hasColor) {
val entityColor = entity.getLightColor()
if (entityColor != null) Color(entityColor) else wearColorScheme.primary
if (entityColor != null) Color(entityColor) else wearColorScheme.surfaceBright
} else {
wearColorScheme.primary
wearColorScheme.surfaceBright
}
return when {
(hasPosition || hasColor) -> {
val checkedStartBackgroundColor = contentBackgroundColor.copy(alpha = 0.5f)
.compositeOver(wearColorScheme.outlineVariant)
val checkedEndBackgroundColor = wearColorScheme.outlineVariant.copy(alpha = 0f)
.compositeOver(wearColorScheme.outlineVariant)
val uncheckedBackgroundColor = wearColorScheme.outlineVariant
val checkedStartBackgroundColor = if (hasColor) {
contentBackgroundColor.copy(alpha = 0.5f).compositeOver(wearColorScheme.surfaceDim)
} else {
wearColorScheme.surfaceBright
}
val checkedEndBackgroundColor = if (hasPosition) {
wearColorScheme.surfaceDim // Used as 'off' color
} else {
checkedStartBackgroundColor // On no position = entire background 'on'
}
val uncheckedBackgroundColor = wearColorScheme.surfaceDim
var checkedBackgroundColors = listOf(
checkedStartBackgroundColor,
@ -156,14 +164,28 @@ object WearToggleChip {
disabledUncheckedBackgroundPaint = WearBrushPainter(Brush.linearGradient(disabledUncheckedBackgroundColors))
}
defaultChipColors().apply {
defaultChipColors(
checkedContentColor = wearColorScheme.onPrimaryContainer,
checkedSecondaryContentColor = wearColorScheme.onPrimaryContainer.copy(alpha = 0.8f),
uncheckedContentColor = wearColorScheme.onSurface,
uncheckedSecondaryContentColor = wearColorScheme.onSurfaceVariant
).apply {
checkedBackgroundPainter = checkedBackgroundPaint
disabledCheckedBackgroundPainter = disabledCheckedBackgroundPaint
uncheckedBackgroundPainter = uncheckedBackgroundPaint
disabledUncheckedBackgroundPainter = disabledUncheckedBackgroundPaint
}
}
else -> ToggleChipDefaults.toggleChipColors()
else -> ToggleChipDefaults.toggleChipColors(
checkedStartBackgroundColor = wearColorScheme.surfaceBright,
checkedEndBackgroundColor = wearColorScheme.surfaceBright,
checkedContentColor = wearColorScheme.onPrimaryContainer,
checkedSecondaryContentColor = wearColorScheme.onPrimaryContainer.copy(alpha = 0.8f),
uncheckedStartBackgroundColor = wearColorScheme.surfaceDim,
uncheckedEndBackgroundColor = wearColorScheme.surfaceDim,
uncheckedContentColor = wearColorScheme.onSurface,
uncheckedSecondaryContentColor = wearColorScheme.onSurfaceVariant
)
}
}