mirror of
https://github.com/home-assistant/android
synced 2024-10-01 13:53:53 +00:00
Update entity button with toggle styling to match M3 components (#3923)
Update entity button styling to match M3 components
This commit is contained in:
parent
fb739596fc
commit
65863148fc
|
@ -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 = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue