Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add haptic + toast feedback to Wear OS details screen #2455

Merged
merged 1 commit into from
Apr 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,11 @@
<string name="show_share_logs_summary">Sharing logs with the Home Assistant team will help to solve issues. Please share the logs only if you have been asked to do so by a Home Assistant developer</string>
<string name="show_share_logs">Show and Share Logs</string>
<string name="sign_in_on_phone">Sign in on phone</string>
<string name="slider_decreased">%1$s decreased</string>
<string name="slider_increased">%1$s increased</string>
<string name="slider_fan_speed">Fan speed</string>
<string name="slider_light_brightness">Brightness</string>
<string name="slider_light_colortemp">Color temperature</string>
<string name="skip">Skip</string>
<string name="speed">Speed: %1$d%%</string>
<string name="state_auto">Auto</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.homeassistant.companion.android.home.views

import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -10,6 +11,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.InlineSlider
Expand All @@ -30,6 +34,8 @@ import io.homeassistant.companion.android.common.data.integration.supportsLightC
import io.homeassistant.companion.android.home.HomePresenterImpl
import io.homeassistant.companion.android.theme.WearAppTheme
import io.homeassistant.companion.android.util.getColorTemperature
import io.homeassistant.companion.android.util.onEntityClickedFeedback
import io.homeassistant.companion.android.util.onEntityFeedback
import java.text.DateFormat

@Composable
Expand All @@ -38,8 +44,13 @@ fun DetailsPanelView(
onEntityToggled: (String, String) -> Unit,
onFanSpeedChanged: (Float) -> Unit,
onBrightnessChanged: (Float) -> Unit,
onColorTempChanged: (Float) -> Unit
onColorTempChanged: (Float) -> Unit,
isToastEnabled: Boolean,
isHapticEnabled: Boolean
) {
val haptic = LocalHapticFeedback.current
val context = LocalContext.current

WearAppTheme {
ThemeLazyColumn {
val attributes = entity.attributes as Map<*, *>
Expand All @@ -55,7 +66,16 @@ fun DetailsPanelView(
val isChecked = entity.state in listOf("on", "locked", "open", "opening")
ToggleButton(
checked = isChecked,
onCheckedChange = { onEntityToggled(entity.entityId, entity.state) },
onCheckedChange = {
onEntityToggled(entity.entityId, entity.state)
onEntityClickedFeedback(
isToastEnabled,
isHapticEnabled,
context,
friendlyName,
haptic
)
},
modifier = Modifier
.padding(start = 16.dp)
.size(ToggleButtonDefaults.SmallToggleButtonSize)
Expand All @@ -69,20 +89,20 @@ fun DetailsPanelView(
if (entity.domain == "fan") {
if (entity.supportsFanSetSpeed()) {
item {
FanSpeedSlider(attributes, onFanSpeedChanged)
FanSpeedSlider(attributes, onFanSpeedChanged, isToastEnabled, isHapticEnabled)
}
}
}
if (entity.domain == "light") {
if (entity.supportsLightBrightness()) {
item {
BrightnessSlider(attributes, onBrightnessChanged)
BrightnessSlider(attributes, onBrightnessChanged, isToastEnabled, isHapticEnabled)
}
}

if (entity.supportsLightColorTemperature() && attributes["color_mode"] == EntityExt.LIGHT_MODE_COLOR_TEMP) {
item {
ColorTempSlider(attributes, onColorTempChanged)
ColorTempSlider(attributes, onColorTempChanged, isToastEnabled, isHapticEnabled)
}
}
}
Expand Down Expand Up @@ -129,7 +149,15 @@ fun DetailsPanelView(
}

@Composable
fun FanSpeedSlider(attributes: Map<*, *>, onFanSpeedChanged: (Float) -> Unit) {
fun FanSpeedSlider(
attributes: Map<*, *>,
onFanSpeedChanged: (Float) -> Unit,
isToastEnabled: Boolean,
isHapticEnabled: Boolean
) {
val haptic = LocalHapticFeedback.current
val context = LocalContext.current

val minValue = 0f
val maxValue = 100f
var currentValue = (attributes["percentage"] as? Number)?.toFloat() ?: 0f
Expand All @@ -147,7 +175,17 @@ fun FanSpeedSlider(attributes: Map<*, *>, onFanSpeedChanged: (Float) -> Unit) {
)
InlineSlider(
value = currentValue,
onValueChange = onFanSpeedChanged,
onValueChange = {
onFanSpeedChanged(it)
onSliderChangedFeedback(
isToastEnabled,
isHapticEnabled,
it > currentValue,
context.getString(R.string.slider_fan_speed),
context,
haptic
)
},
steps = 9,
valueRange = minValue..maxValue,
decreaseIcon = {
Expand All @@ -168,7 +206,15 @@ fun FanSpeedSlider(attributes: Map<*, *>, onFanSpeedChanged: (Float) -> Unit) {
}

@Composable
fun BrightnessSlider(attributes: Map<*, *>, onBrightnessChanged: (Float) -> Unit) {
fun BrightnessSlider(
attributes: Map<*, *>,
onBrightnessChanged: (Float) -> Unit,
isToastEnabled: Boolean,
isHapticEnabled: Boolean
) {
val haptic = LocalHapticFeedback.current
val context = LocalContext.current

val minValue = 0f
val maxValue = 100f
var currentValue =
Expand All @@ -190,6 +236,14 @@ fun BrightnessSlider(attributes: Map<*, *>, onBrightnessChanged: (Float) -> Unit
value = currentValue,
onValueChange = { brightness ->
onBrightnessChanged(brightness.div(100).times(255))
onSliderChangedFeedback(
isToastEnabled,
isHapticEnabled,
brightness > currentValue,
context.getString(R.string.slider_light_brightness),
context,
haptic
)
},
steps = 20,
valueRange = minValue..maxValue,
Expand All @@ -211,7 +265,15 @@ fun BrightnessSlider(attributes: Map<*, *>, onBrightnessChanged: (Float) -> Unit
}

@Composable
fun ColorTempSlider(attributes: Map<*, *>, onColorTempChanged: (Float) -> Unit) {
fun ColorTempSlider(
attributes: Map<*, *>,
onColorTempChanged: (Float) -> Unit,
isToastEnabled: Boolean,
isHapticEnabled: Boolean
) {
val haptic = LocalHapticFeedback.current
val context = LocalContext.current

val minValue = (attributes["min_mireds"] as? Number)?.toFloat() ?: 0f
val maxValue = (attributes["max_mireds"] as? Number)?.toFloat() ?: 0f
var currentValue = (attributes["color_temp"] as? Number)?.toFloat() ?: 0f
Expand All @@ -229,7 +291,17 @@ fun ColorTempSlider(attributes: Map<*, *>, onColorTempChanged: (Float) -> Unit)
)
InlineSlider(
value = currentValue,
onValueChange = onColorTempChanged,
onValueChange = {
onColorTempChanged(it)
onSliderChangedFeedback(
isToastEnabled,
isHapticEnabled,
it > currentValue,
context.getString(R.string.slider_light_colortemp),
context,
haptic
)
},
steps = 20,
valueRange = minValue..maxValue,
decreaseIcon = {
Expand All @@ -253,3 +325,23 @@ fun ColorTempSlider(attributes: Map<*, *>, onColorTempChanged: (Float) -> Unit)
)
}
}

private fun onSliderChangedFeedback(
isToastEnabled: Boolean,
isHapticEnabled: Boolean,
increase: Boolean,
sliderName: String,
context: Context,
haptic: HapticFeedback
) {
val fullMessage =
if (increase) context.getString(R.string.slider_increased, sliderName)
else context.getString(R.string.slider_decreased, sliderName)
onEntityFeedback(
isToastEnabled,
isHapticEnabled,
fullMessage,
context,
haptic
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ fun LoadHomePage(
entity.entityId,
colorTemp
)
}
},
isToastEnabled = mainViewModel.isToastEnabled.value,
isHapticEnabled = mainViewModel.isHapticEnabled.value
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,13 @@ private fun coverIcon(state: String?, entity: Entity<Map<String, Any>>?): IIcon?
}

fun onEntityClickedFeedback(isToastEnabled: Boolean, isHapticEnabled: Boolean, context: Context, friendlyName: String, haptic: HapticFeedback) {
val message = context.getString(commonR.string.toast_message, friendlyName)
onEntityFeedback(isToastEnabled, isHapticEnabled, message, context, haptic)
}

fun onEntityFeedback(isToastEnabled: Boolean, isHapticEnabled: Boolean, message: String, context: Context, haptic: HapticFeedback) {
if (isToastEnabled)
Toast.makeText(context, context.getString(commonR.string.toast_message, friendlyName), Toast.LENGTH_SHORT).show()
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
if (isHapticEnabled)
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}