Skip to content

Commit

Permalink
Add settings for wear haptic feedback and toast on entity selection (#…
Browse files Browse the repository at this point in the history
…1893)

* Add settings for wear haptic feedback and toast on entity selection

* Lint

* Implement review suggestions.

* Add preview functions for easy UI viewing in Android Studio (#1901)

* Add settings for wear haptic feedback and toast on entity selection

* Merge in review fixes

* Lint

* Remove duplicate calls

Co-authored-by: Justin Bassett <[email protected]>
  • Loading branch information
dshokouhi and JBassett authored Nov 11, 2021
1 parent b060f0c commit 27627fa
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ interface IntegrationRepository {

suspend fun setWearHomeFavorites(favorites: Set<String>)
suspend fun getWearHomeFavorites(): Set<String>
suspend fun setWearHapticFeedback(enabled: Boolean)
suspend fun getWearHapticFeedback(): Boolean
suspend fun setWearToastConfirmation(enabled: Boolean)
suspend fun getWearToastConfirmation(): Boolean

suspend fun getHomeAssistantVersion(): String

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class IntegrationRepositoryImpl @Inject constructor(

private const val PREF_CHECK_SENSOR_REGISTRATION_NEXT = "sensor_reg_last"
private const val PREF_WEAR_HOME_FAVORITES = "wear_home_favorites"
private const val PREF_WEAR_HAPTIC_FEEDBACK = "wear_haptic_feedback"
private const val PREF_WEAR_TOAST_CONFIRMATION = "wear_toast_confirmation"
private const val PREF_HA_VERSION = "ha_version"
private const val PREF_AUTOPLAY_VIDEO = "autoplay_video"
private const val PREF_FULLSCREEN_ENABLED = "fullscreen_enabled"
Expand Down Expand Up @@ -352,6 +354,22 @@ class IntegrationRepositoryImpl @Inject constructor(
return localStorage.getStringSet(PREF_WEAR_HOME_FAVORITES) ?: setOf()
}

override suspend fun setWearHapticFeedback(enabled: Boolean) {
localStorage.putBoolean(PREF_WEAR_HAPTIC_FEEDBACK, enabled)
}

override suspend fun getWearHapticFeedback(): Boolean {
return localStorage.getBoolean(PREF_WEAR_HAPTIC_FEEDBACK)
}

override suspend fun setWearToastConfirmation(enabled: Boolean) {
localStorage.putBoolean(PREF_WEAR_TOAST_CONFIRMATION, enabled)
}

override suspend fun getWearToastConfirmation(): Boolean {
return localStorage.getBoolean(PREF_WEAR_TOAST_CONFIRMATION)
}

override suspend fun getNotificationRateLimits(): RateLimitResponse {
val pushToken = localStorage.getString(PREF_PUSH_TOKEN) ?: ""
val requestBody = RateLimitRequest(pushToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ interface HomePresenter {
suspend fun getEntities(): List<Entity<*>>
suspend fun getWearHomeFavorites(): List<String>
suspend fun setWearHomeFavorites(favorites: List<String>)

suspend fun getWearHapticFeedback(): Boolean
suspend fun setWearHapticFeedback(enabled: Boolean)
suspend fun getWearToastConfirmation(): Boolean
suspend fun setWearToastConfirmation(enabled: Boolean)
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,20 @@ class HomePresenterImpl @Inject constructor(
override suspend fun setWearHomeFavorites(favorites: List<String>) {
integrationUseCase.setWearHomeFavorites(favorites.toSet())
}

override suspend fun getWearHapticFeedback(): Boolean {
return integrationUseCase.getWearHapticFeedback()
}

override suspend fun setWearHapticFeedback(enabled: Boolean) {
integrationUseCase.setWearHapticFeedback(enabled)
}

override suspend fun getWearToastConfirmation(): Boolean {
return integrationUseCase.getWearToastConfirmation()
}

override suspend fun setWearToastConfirmation(enabled: Boolean) {
integrationUseCase.setWearToastConfirmation(enabled)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.homeassistant.companion.android.home

import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.homeassistant.companion.android.common.data.integration.Entity
Expand All @@ -20,10 +21,16 @@ class MainViewModel : ViewModel() {
private set
var favoriteEntityIds = mutableStateListOf<String>()
private set
var isHapticEnabled = mutableStateOf(false)
private set
var isToastEnabled = mutableStateOf(false)
private set

fun loadEntities() {
private fun loadEntities() {
viewModelScope.launch {
favoriteEntityIds.addAll(homePresenter.getWearHomeFavorites())
isHapticEnabled.value = homePresenter.getWearHapticFeedback()
isToastEnabled.value = homePresenter.getWearToastConfirmation()
entities.addAll(homePresenter.getEntities())
}
}
Expand Down Expand Up @@ -62,6 +69,20 @@ class MainViewModel : ViewModel() {
}
}

fun setHapticEnabled(enabled: Boolean) {
viewModelScope.launch {
homePresenter.setWearHapticFeedback(enabled)
isHapticEnabled.value = enabled
}
}

fun setToastEnabled(enabled: Boolean) {
viewModelScope.launch {
homePresenter.setWearToastConfirmation(enabled)
isToastEnabled.value = enabled
}
}

fun logout() {
homePresenter.onLogoutClicked()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
Expand All @@ -21,29 +22,38 @@ import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.home.HomePresenterImpl
import io.homeassistant.companion.android.util.getIcon
import io.homeassistant.companion.android.util.onEntityClickedFeedback
import io.homeassistant.companion.android.util.previewEntity1
import io.homeassistant.companion.android.util.previewEntity2
import io.homeassistant.companion.android.util.setChipDefaults

@Composable
fun EntityUi(
entity: Entity<*>,
onEntityClicked: (String) -> Unit
onEntityClicked: (String) -> Unit,
isHapticEnabled: Boolean,
isToastEnabled: Boolean
) {
val haptic = LocalHapticFeedback.current
val context = LocalContext.current
val attributes = entity.attributes as Map<*, *>
val iconBitmap = getIcon(attributes["icon"] as String?, entity.entityId.split(".")[0], LocalContext.current)
val friendlyName = attributes["friendly_name"].toString()

if (entity.entityId.split(".")[0] in HomePresenterImpl.toggleDomains) {
ToggleChip(
checked = entity.state == "on",
onCheckedChange = { onEntityClicked(entity.entityId) },
onCheckedChange = {
onEntityClicked(entity.entityId)
onEntityClickedFeedback(isToastEnabled, isHapticEnabled, context, friendlyName, haptic)
},
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
appIcon = { Image(asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone) },
label = {
Text(
text = attributes["friendly_name"].toString(),
text = friendlyName,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
Expand All @@ -69,13 +79,16 @@ fun EntityUi(
icon = { Image(asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone) },
label = {
Text(
text = attributes["friendly_name"].toString(),
text = friendlyName,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
enabled = entity.state != "unavailable",
onClick = { onEntityClicked(entity.entityId) },
onClick = {
onEntityClicked(entity.entityId)
onEntityClickedFeedback(isToastEnabled, isHapticEnabled, context, friendlyName, haptic)
},
colors = setChipDefaults()
)
}
Expand All @@ -87,11 +100,15 @@ private fun PreviewEntityUI() {
Column {
EntityUi(
entity = previewEntity1,
onEntityClicked = {}
onEntityClicked = {},
isHapticEnabled = true,
isToastEnabled = false
)
EntityUi(
entity = previewEntity2,
onEntityClicked = {}
onEntityClicked = {},
isHapticEnabled = false,
isToastEnabled = true
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,20 @@ fun LoadHomePage(
mainViewModel.favoriteEntityIds,
{ mainViewModel.toggleEntity(it) },
{ swipeDismissableNavController.navigate(SCREEN_SETTINGS) },
{ mainViewModel.logout() }
{ mainViewModel.logout() },
mainViewModel.isHapticEnabled.value,
mainViewModel.isToastEnabled.value
)
}
composable(SCREEN_SETTINGS) {
SettingsView(
mainViewModel.favoriteEntityIds,
{ swipeDismissableNavController.navigate(SCREEN_SET_FAVORITES) },
{ mainViewModel.clearFavorites() }
{ mainViewModel.clearFavorites() },
mainViewModel.isHapticEnabled.value,
mainViewModel.isToastEnabled.value,
{ mainViewModel.setHapticEnabled(it) },
{ mainViewModel.setToastEnabled(it) }
)
}
composable(SCREEN_SET_FAVORITES) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -37,6 +39,7 @@ import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.util.LocalRotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventState
import io.homeassistant.companion.android.util.onEntityClickedFeedback
import io.homeassistant.companion.android.util.previewEntityList
import io.homeassistant.companion.android.util.previewFavoritesList
import io.homeassistant.companion.android.util.setChipDefaults
Expand All @@ -48,7 +51,9 @@ fun MainView(
favoriteEntityIds: List<String>,
onEntityClicked: (String) -> Unit,
onSettingsClicked: () -> Unit,
onLogoutClicked: () -> Unit
onLogoutClicked: () -> Unit,
isHapticEnabled: Boolean,
isToastEnabled: Boolean
) {
val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()

Expand All @@ -67,6 +72,8 @@ fun MainView(
val inputBooleans = entities.filter { it.entityId.split(".")[0] == "input_boolean" }
val switches = entities.filter { it.entityId.split(".")[0] == "switch" }

val haptic = LocalHapticFeedback.current
val context = LocalContext.current
RotaryEventDispatcher(scalingLazyListState)
RotaryEventState(scrollState = scalingLazyListState)

Expand Down Expand Up @@ -121,7 +128,10 @@ fun MainView(
overflow = TextOverflow.Ellipsis
)
},
onClick = { onEntityClicked(favoriteEntityID) },
onClick = {
onEntityClicked(favoriteEntityID)
onEntityClickedFeedback(isToastEnabled, isHapticEnabled, context, favoriteEntityID, haptic)
},
colors = ChipDefaults.primaryChipColors(
backgroundColor = colorResource(id = R.color.colorAccent),
contentColor = Color.Black
Expand All @@ -130,7 +140,9 @@ fun MainView(
} else {
EntityUi(
entityMap[favoriteEntityID]!!,
onEntityClicked
onEntityClicked,
isHapticEnabled,
isToastEnabled
)
}
}
Expand Down Expand Up @@ -169,7 +181,7 @@ fun MainView(
}
if (expandedInputBooleans) {
items(inputBooleans.size) { index ->
EntityUi(inputBooleans[index], onEntityClicked)
EntityUi(inputBooleans[index], onEntityClicked, isHapticEnabled, isToastEnabled)
}
}
}
Expand All @@ -183,7 +195,7 @@ fun MainView(
}
if (expandedLights) {
items(lights.size) { index ->
EntityUi(lights[index], onEntityClicked)
EntityUi(lights[index], onEntityClicked, isHapticEnabled, isToastEnabled)
}
}
}
Expand All @@ -197,7 +209,7 @@ fun MainView(
}
if (expandedScenes) {
items(scenes.size) { index ->
EntityUi(scenes[index], onEntityClicked)
EntityUi(scenes[index], onEntityClicked, isHapticEnabled, isToastEnabled)
}
}
}
Expand All @@ -211,7 +223,7 @@ fun MainView(
}
if (expandedScripts) {
items(scripts.size) { index ->
EntityUi(scripts[index], onEntityClicked)
EntityUi(scripts[index], onEntityClicked, isHapticEnabled, isToastEnabled)
}
}
}
Expand All @@ -225,7 +237,7 @@ fun MainView(
}
if (expandedSwitches) {
items(switches.size) { index ->
EntityUi(switches[index], onEntityClicked)
EntityUi(switches[index], onEntityClicked, isHapticEnabled, isToastEnabled)
}
}
}
Expand All @@ -252,7 +264,10 @@ private fun PreviewMainView() {
entities = previewEntityList,
favoriteEntityIds = previewFavoritesList,
onEntityClicked = {},
onSettingsClicked = {}
) {}
onSettingsClicked = {},
onLogoutClicked = {},
isHapticEnabled = true,
isToastEnabled = false
)
}
}
Loading

0 comments on commit 27627fa

Please sign in to comment.