Skip to content

Commit

Permalink
Update entity button with toggle styling to match M3 components (#3923)
Browse files Browse the repository at this point in the history
Update entity button styling to match M3 components
  • Loading branch information
jpelgrom authored Oct 6, 2023
1 parent fb73959 commit 6586314
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -46,65 +48,63 @@ 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,
colorFilter = ColorFilter.tint(wearColorScheme.onSurface)
)
},
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)
}
)
}
)
},
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
)
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
)
}
},
colors = WearToggleChip.entityToggleChipBackgroundColors(entity, isChecked)
enabled = isEnabled,
toggleControl = { ToggleSwitch(isChecked) },
colors = colors
)
} else {
Button(
modifier = Modifier
.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
icon = {
Image(
asset = iconBitmap,
Expand All @@ -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,
Expand Down Expand Up @@ -163,5 +147,12 @@ private fun PreviewEntityUI() {
isToastEnabled = true,
onEntityLongPressed = { }
)
EntityUi(
entity = previewEntity4,
onEntityClicked = { _, _ -> },
isHapticEnabled = false,
isToastEnabled = true,
onEntityLongPressed = { }
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
)
}
}

Expand Down

0 comments on commit 6586314

Please sign in to comment.