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 settings for wear haptic feedback and toast on entity selection #1893

Merged
merged 9 commits into from Nov 11, 2021
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
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
1 change: 1 addition & 0 deletions wear/build.gradle.kts
Expand Up @@ -99,6 +99,7 @@ dependencies {
implementation("androidx.activity:activity-compose:1.4.0")
implementation("androidx.compose.compiler:compiler:1.0.5")
implementation("androidx.compose.foundation:foundation:1.0.5")
implementation("androidx.compose.ui:ui-tooling:1.0.5")
implementation("androidx.wear.compose:compose-foundation:1.0.0-alpha10")
implementation("androidx.wear.compose:compose-material:1.0.0-alpha10")
implementation("androidx.wear.compose:compose-navigation:1.0.0-alpha10")
Expand Down
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)
}
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)
}
}
@@ -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
@@ -1,13 +1,16 @@
package io.homeassistant.companion.android.home.views

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
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
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.Text
Expand All @@ -19,27 +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 @@ -65,14 +79,36 @@ 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()
)
}
}

@Preview
@Composable
private fun PreviewEntityUI() {
Column {
EntityUi(
entity = previewEntity1,
onEntityClicked = {},
isHapticEnabled = true,
isToastEnabled = false
)
EntityUi(
entity = previewEntity2,
onEntityClicked = {},
isHapticEnabled = false,
isToastEnabled = true
)
}
}
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ExperimentalWearMaterialApi
Expand Down Expand Up @@ -67,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 All @@ -96,3 +103,10 @@ fun LoadHomePage(
}
}
}

@ExperimentalWearMaterialApi
@Preview
@Composable
private fun PreviewHomeView() {
LoadHomePage(mainViewModel = MainViewModel())
}
Expand Up @@ -6,8 +6,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.wear.compose.material.ListHeader
import androidx.wear.compose.material.Text
import io.homeassistant.companion.android.R

@Composable
fun ListHeader(
Expand Down Expand Up @@ -39,3 +41,13 @@ fun ListHeader(id: Int) {
}
}
}

@Preview
@Composable
private fun PreviewListHeader() {
ListHeader(
stringId = R.string.other,
expanded = true,
onExpandChanged = {}
)
}