Add settings and favorites for wear so users can quickly execute actions they do regularly faster (#1875)

* Add settings and favorites for wear

* Use proper variable for navigation

* Refactoring to remove clutter from HomeActivity

* Always start set favorites at the top

* Review comments

* Correct merge mistake

* Add rotary input on set favorites screen
This commit is contained in:
Daniel Shokouhi 2021-11-09 06:41:23 -08:00 committed by GitHub
parent 588e74113d
commit 2c5e762504
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 486 additions and 115 deletions

View file

@ -32,6 +32,9 @@ interface IntegrationRepository {
suspend fun setSessionExpireMillis(value: Long)
suspend fun getSessionExpireMillis(): Long
suspend fun setWearHomeFavorites(favorites: Set<String>)
suspend fun getWearHomeFavorites(): Set<String>
suspend fun getThemeColor(): String
suspend fun getHomeAssistantVersion(): String

View file

@ -55,7 +55,7 @@ class IntegrationRepositoryImpl @Inject constructor(
private const val PREF_SECRET = "secret"
private const val PREF_CHECK_SENSOR_REGISTRATION_NEXT = "sensor_reg_last"
private const val PREF_INSTALLED_APP_VERSION = "installed_app_version"
private const val PREF_WEAR_HOME_FAVORITES = "wear_home_favorites"
private const val PREF_HA_VERSION = "ha_version"
private const val PREF_AUTOPLAY_VIDEO = "autoplay_video"
private const val PREF_FULLSCREEN_ENABLED = "fullscreen_enabled"
@ -343,6 +343,14 @@ class IntegrationRepositoryImpl @Inject constructor(
return localStorage.getLong(PREF_SESSION_EXPIRE) ?: 0
}
override suspend fun setWearHomeFavorites(favorites: Set<String>) {
localStorage.putStringSet(PREF_WEAR_HOME_FAVORITES, favorites)
}
override suspend fun getWearHomeFavorites(): Set<String> {
return localStorage.getStringSet(PREF_WEAR_HOME_FAVORITES) ?: setOf()
}
override suspend fun getNotificationRateLimits(): RateLimitResponse {
val pushToken = localStorage.getString(PREF_PUSH_TOKEN) ?: ""
val requestBody = RateLimitRequest(pushToken)

View file

@ -101,4 +101,5 @@ dependencies {
implementation("androidx.compose.foundation:foundation: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")
}

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@ -19,14 +20,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipColors
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.ExperimentalWearMaterialApi
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.PositionIndicator
import androidx.wear.compose.material.Scaffold
@ -36,7 +36,9 @@ import androidx.wear.compose.material.Text
import androidx.wear.compose.material.ToggleChip
import androidx.wear.compose.material.ToggleChipDefaults
import androidx.wear.compose.material.rememberScalingLazyListState
import com.mikepenz.iconics.IconicsDrawable
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.DaggerPresenterComponent
@ -46,10 +48,16 @@ import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.onboarding.OnboardingActivity
import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity
import io.homeassistant.companion.android.settings.ScreenSetFavorites
import io.homeassistant.companion.android.settings.ScreenSettings
import io.homeassistant.companion.android.util.LocalRotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventHandlerSetup
import io.homeassistant.companion.android.util.RotaryEventState
import io.homeassistant.companion.android.util.SetTitle
import io.homeassistant.companion.android.util.getIcon
import io.homeassistant.companion.android.util.setChipDefaults
import io.homeassistant.companion.android.util.updateFavorites
import io.homeassistant.companion.android.viewModels.EntityViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -68,12 +76,16 @@ class HomeActivity : ComponentActivity(), HomeView {
companion object {
private const val TAG = "HomeActivity"
private const val SCREEN_LANDING = "landing"
const val SCREEN_SETTINGS = "settings"
const val SCREEN_SET_FAVORITES = "set_favorites"
fun newInstance(context: Context): Intent {
return Intent(context, HomeActivity::class.java)
}
}
@ExperimentalWearMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -86,8 +98,9 @@ class HomeActivity : ComponentActivity(), HomeView {
presenter.onViewReady()
updateEntities()
updateFavorites(entityViewModel, presenter, mainScope)
setContent {
LoadHomePage(entities = entityViewModel.entitiesResponse)
LoadHomePage(entities = entityViewModel.entitiesResponse, entityViewModel.favoriteEntities)
}
}
@ -108,11 +121,12 @@ class HomeActivity : ComponentActivity(), HomeView {
finish()
}
@ExperimentalWearMaterialApi
@Composable
private fun LoadHomePage(entities: Array<Entity<Any>>) {
private fun LoadHomePage(entities: Array<Entity<Any>>, favorites: MutableSet<String>) {
val rotaryEventDispatcher = RotaryEventDispatcher()
if (entities.isNullOrEmpty()) {
if (entities.isNullOrEmpty() && favorites.isNullOrEmpty()) {
Column {
Spacer(
modifier = Modifier
@ -134,6 +148,9 @@ class HomeActivity : ComponentActivity(), HomeView {
)
}
} else {
updateFavorites(entityViewModel, presenter, mainScope)
val validEntities =
entities.sortedBy { it.entityId }.filter { it.entityId.split(".")[0] in HomePresenterImpl.supportedDomains }
val scenes =
entities.sortedBy { it.entityId }.filter { it.entityId.split(".")[0] == "scene" }
val scripts =
@ -147,6 +164,7 @@ class HomeActivity : ComponentActivity(), HomeView {
val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
RotaryEventDispatcher(scalingLazyListState)
val swipeDismissableNavController = rememberSwipeDismissableNavController()
MaterialTheme {
Scaffold(
@ -159,79 +177,156 @@ class HomeActivity : ComponentActivity(), HomeView {
LocalRotaryEventDispatcher provides rotaryEventDispatcher
) {
RotaryEventHandlerSetup(rotaryEventDispatcher)
RotaryEventState(scrollState = scalingLazyListState)
ScalingLazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
top = 10.dp,
start = 10.dp,
end = 10.dp,
bottom = 40.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
state = scalingLazyListState
SwipeDismissableNavHost(
navController = swipeDismissableNavController,
startDestination = SCREEN_LANDING
) {
if (inputBooleans.isNotEmpty()) {
items(inputBooleans.size) { index ->
if (index == 0)
SetTitle(R.string.input_booleans)
SetEntityUI(inputBooleans[index], index)
}
}
if (lights.isNotEmpty()) {
items(lights.size) { index ->
if (index == 0)
SetTitle(R.string.lights)
SetEntityUI(lights[index], index)
}
}
if (scenes.isNotEmpty()) {
items(scenes.size) { index ->
if (index == 0)
SetTitle(R.string.scenes)
composable(SCREEN_LANDING) {
RotaryEventState(scrollState = scalingLazyListState)
ScalingLazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
top = 10.dp,
start = 10.dp,
end = 10.dp,
bottom = 40.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
state = scalingLazyListState
) {
if (favorites.isNotEmpty()) {
val favoriteArray = favorites.toTypedArray()
items(favoriteArray.size) { index ->
Spacer(modifier = Modifier.height(10.dp))
if (index == 0)
SetTitle(id = R.string.favorites)
val favoriteEntityID =
favoriteArray[index].split(",")[0]
val favoriteName = favoriteArray[index].split(",")[1]
val favoriteIcon = favoriteArray[index].split(",")[2]
if (entities.isNullOrEmpty()) {
// Use a normal chip when we don't have the state of the entity
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = if (index == 0) 30.dp else 10.dp),
icon = {
Image(
asset = getIcon(
favoriteIcon,
favoriteEntityID.split(".")[0],
baseContext
)
?: CommunityMaterial.Icon.cmd_cellphone
)
},
label = {
Text(
text = favoriteName,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
onClick = {
presenter.onEntityClicked(
favoriteEntityID
)
},
colors = ChipDefaults.primaryChipColors(
backgroundColor = colorResource(id = R.color.colorAccent),
contentColor = Color.Black
)
)
} else {
for (entity in entities) {
if (entity.entityId == favoriteEntityID) {
SetEntityUI(entity = entity, index = index)
}
}
}
}
}
if (entities.isNullOrEmpty()) {
item {
Column {
SetTitle(id = R.string.loading)
Chip(
modifier = Modifier
.padding(
top = 10.dp,
start = 10.dp,
end = 10.dp
),
label = {
Text(
text = stringResource(R.string.loading_entities),
textAlign = TextAlign.Center
)
},
onClick = { /* No op */ },
colors = setChipDefaults()
)
}
}
}
if (inputBooleans.isNotEmpty()) {
items(inputBooleans.size) { index ->
if (index == 0)
SetTitle(R.string.input_booleans)
SetEntityUI(inputBooleans[index], index)
}
}
if (lights.isNotEmpty()) {
items(lights.size) { index ->
if (index == 0)
SetTitle(R.string.lights)
SetEntityUI(lights[index], index)
}
}
if (scenes.isNotEmpty()) {
items(scenes.size) { index ->
if (index == 0)
SetTitle(R.string.scenes)
SetEntityUI(scenes[index], index)
}
}
if (scripts.isNotEmpty()) {
items(scripts.size) { index ->
if (index == 0)
SetTitle(R.string.scripts)
SetEntityUI(scripts[index], index)
}
}
if (switches.isNotEmpty()) {
items(switches.size) { index ->
if (index == 0)
SetTitle(R.string.switches)
SetEntityUI(switches[index], index)
}
}
SetEntityUI(scenes[index], index)
}
}
if (scripts.isNotEmpty()) {
items(scripts.size) { index ->
if (index == 0)
SetTitle(R.string.scripts)
SetEntityUI(scripts[index], index)
}
}
if (switches.isNotEmpty()) {
items(switches.size) { index ->
if (index == 0)
SetTitle(R.string.switches)
SetEntityUI(switches[index], index)
}
}
item {
Column {
SetTitle(R.string.other)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_exit_run)
},
label = {
Text(
text = stringResource(id = R.string.logout)
)
},
onClick = { presenter.onLogoutClicked() },
colors = ChipDefaults.primaryChipColors(
backgroundColor = Color.Red,
contentColor = Color.Black
)
)
item {
LoadOtherSection(swipeDismissableNavController)
}
}
}
composable(SCREEN_SETTINGS) {
ScreenSettings(
swipeDismissableNavController,
entityViewModel,
presenter
)
}
composable(SCREEN_SET_FAVORITES) {
ScreenSetFavorites(
validEntities,
entityViewModel,
baseContext,
presenter
)
}
}
}
}
@ -242,25 +337,13 @@ class HomeActivity : ComponentActivity(), HomeView {
@Composable
private fun SetEntityUI(entity: Entity<Any>, index: Int) {
val attributes = entity.attributes as Map<String, String>
val iconBitmap =
if (attributes["icon"]?.startsWith("mdi") == true) {
val icon = attributes["icon"]!!.split(":")[1]
IconicsDrawable(baseContext, "cmd-$icon").icon
} else {
when (entity.entityId.split(".")[0]) {
"input_boolean", "switch" -> CommunityMaterial.Icon2.cmd_light_switch
"light" -> CommunityMaterial.Icon2.cmd_lightbulb
"script" -> CommunityMaterial.Icon3.cmd_script_text_outline
"scene" -> CommunityMaterial.Icon3.cmd_palette_outline
else -> CommunityMaterial.Icon.cmd_cellphone
}
}
val iconBitmap = getIcon(attributes["icon"], entity.entityId.split(".")[0], baseContext)
if (entity.entityId.split(".")[0] in HomePresenterImpl.toggleDomains) {
ToggleChip(
checked = entity.state == "on",
onCheckedChange = {
presenter.onEntityClicked(entity)
presenter.onEntityClicked(entity.entityId)
updateEntities()
},
modifier = Modifier
@ -302,7 +385,7 @@ class HomeActivity : ComponentActivity(), HomeView {
},
enabled = entity.state != "unavailable",
onClick = {
presenter.onEntityClicked(entity)
presenter.onEntityClicked(entity.entityId)
updateEntities()
},
colors = setChipDefaults()
@ -311,23 +394,45 @@ class HomeActivity : ComponentActivity(), HomeView {
}
@Composable
private fun SetTitle(id: Int) {
Text(
text = stringResource(id = id),
textAlign = TextAlign.Center,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.fillMaxWidth()
)
}
@Composable
private fun setChipDefaults(): ChipColors {
return ChipDefaults.primaryChipColors(
backgroundColor = colorResource(id = R.color.colorAccent),
contentColor = Color.Black
)
private fun LoadOtherSection(swipeDismissableNavController: NavHostController) {
Column {
SetTitle(R.string.other)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_cog)
},
label = {
Text(
text = stringResource(id = R.string.settings)
)
},
onClick = { swipeDismissableNavController.navigate(SCREEN_SETTINGS) },
colors = ChipDefaults.primaryChipColors(
contentColor = Color.Black
)
)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_exit_run)
},
label = {
Text(
text = stringResource(id = R.string.logout)
)
},
onClick = { presenter.onLogoutClicked() },
colors = ChipDefaults.primaryChipColors(
backgroundColor = Color.Red,
contentColor = Color.Black
)
)
}
}
private fun updateEntities() {

View file

@ -5,8 +5,10 @@ import io.homeassistant.companion.android.common.data.integration.Entity
interface HomePresenter {
fun onViewReady()
fun onEntityClicked(entity: Entity<Any>)
fun onEntityClicked(entityId: String)
fun onLogoutClicked()
fun onFinish()
suspend fun getEntities(): Array<Entity<Any>>
suspend fun getWearHomeFavorites(): Set<String>
suspend fun setWearHomeFavorites(favorites: Set<String>)
}

View file

@ -25,6 +25,9 @@ class HomePresenterImpl @Inject constructor(
"cover", "fan", "humidifier", "input_boolean", "light",
"media_player", "remote", "siren", "switch"
)
val supportedDomains = listOf(
"input_boolean", "light", "switch", "script", "scene"
)
const val TAG = "HomePresenter"
}
@ -52,22 +55,22 @@ class HomePresenterImpl @Inject constructor(
}
}
override fun onEntityClicked(entity: Entity<Any>) {
override fun onEntityClicked(entityId: String) {
if (entity.entityId.split(".")[0] in toggleDomains) {
if (entityId.split(".")[0] in toggleDomains) {
mainScope.launch {
integrationUseCase.callService(
entity.entityId.split(".")[0],
entityId.split(".")[0],
"toggle",
hashMapOf("entity_id" to entity.entityId)
hashMapOf("entity_id" to entityId)
)
}
} else {
mainScope.launch {
integrationUseCase.callService(
entity.entityId.split(".")[0],
entityId.split(".")[0],
"turn_on",
hashMapOf("entity_id" to entity.entityId)
hashMapOf("entity_id" to entityId)
)
}
}
@ -99,4 +102,12 @@ class HomePresenterImpl @Inject constructor(
}
}
}
override suspend fun getWearHomeFavorites(): Set<String> {
return integrationUseCase.getWearHomeFavorites()
}
override suspend fun setWearHomeFavorites(favorites: Set<String>) {
integrationUseCase.setWearHomeFavorites(favorites)
}
}

View file

@ -0,0 +1,168 @@
package io.homeassistant.companion.android.settings
import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
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.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.ScalingLazyColumn
import androidx.wear.compose.material.ScalingLazyListState
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.ToggleChip
import androidx.wear.compose.material.ToggleChipDefaults
import androidx.wear.compose.material.rememberScalingLazyListState
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.home.HomeActivity
import io.homeassistant.companion.android.home.HomePresenter
import io.homeassistant.companion.android.util.RotaryEventState
import io.homeassistant.companion.android.util.SetTitle
import io.homeassistant.companion.android.util.getIcon
import io.homeassistant.companion.android.util.saveFavorites
import io.homeassistant.companion.android.viewModels.EntityViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
@Composable
fun ScreenSettings(swipeDismissableNavController: NavHostController, entityViewModel: EntityViewModel, presenter: HomePresenter) {
Column {
Spacer(modifier = Modifier.height(20.dp))
SetTitle(id = R.string.settings)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp),
icon = {
Image(asset = CommunityMaterial.Icon3.cmd_star)
},
label = {
Text(
text = stringResource(id = R.string.favorite)
)
},
onClick = {
swipeDismissableNavController.navigate(
HomeActivity.SCREEN_SET_FAVORITES
)
},
colors = ChipDefaults.primaryChipColors(
contentColor = Color.Black
)
)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_delete)
},
label = {
Text(
text = stringResource(id = R.string.clear_favorites),
)
},
onClick = {
entityViewModel.favoriteEntities = mutableSetOf()
saveFavorites(entityViewModel.favoriteEntities.toMutableSet(), presenter, mainScope)
},
colors = ChipDefaults.primaryChipColors(
contentColor = Color.Black
),
secondaryLabel = {
Text(
text = stringResource(id = R.string.irreverisble)
)
},
enabled = entityViewModel.favoriteEntities.isNotEmpty()
)
}
}
@Composable
fun ScreenSetFavorites(
validEntities: List<Entity<Any>>,
entityViewModel: EntityViewModel,
context: Context,
presenter: HomePresenter
) {
val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
RotaryEventState(scrollState = scalingLazyListState)
ScalingLazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
top = 10.dp,
start = 10.dp,
end = 10.dp,
bottom = 40.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
state = scalingLazyListState
) {
items(validEntities.size) { index ->
val attributes = validEntities[index].attributes as Map<String, String>
val iconBitmap = getIcon(attributes["icon"], validEntities[index].entityId.split(".")[0], context)
if (index == 0)
SetTitle(id = R.string.set_favorite)
val elementString = "${validEntities[index].entityId},${attributes["friendly_name"]},${attributes["icon"]}"
var checked by rememberSaveable { mutableStateOf(entityViewModel.favoriteEntities.contains(elementString)) }
ToggleChip(
checked = checked,
onCheckedChange = {
checked = it
if (it) {
entityViewModel.favoriteEntities.add(elementString)
} else {
entityViewModel.favoriteEntities.remove(elementString)
}
saveFavorites(entityViewModel.favoriteEntities.toMutableSet(), presenter, mainScope)
},
modifier = Modifier
.fillMaxWidth()
.padding(top = if (index == 0) 30.dp else 10.dp),
appIcon = { Image(asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone) },
label = {
Text(
text = attributes["friendly_name"].toString(),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
toggleIcon = { ToggleChipDefaults.SwitchIcon(checked) },
colors = ToggleChipDefaults.toggleChipColors(
checkedStartBackgroundColor = colorResource(id = R.color.colorAccent),
checkedEndBackgroundColor = colorResource(id = R.color.colorAccent),
uncheckedStartBackgroundColor = colorResource(id = R.color.colorAccent),
uncheckedEndBackgroundColor = colorResource(id = R.color.colorAccent),
checkedContentColor = Color.Black,
uncheckedContentColor = Color.Black,
checkedToggleIconTintColor = Color.Yellow,
uncheckedToggleIconTintColor = Color.DarkGray
)
)
}
}
}

View file

@ -0,0 +1,66 @@
package io.homeassistant.companion.android.util
import android.content.Context
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp
import androidx.wear.compose.material.ChipColors
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.Text
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.home.HomePresenter
import io.homeassistant.companion.android.viewModels.EntityViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
fun SetTitle(id: Int) {
Text(
text = stringResource(id = id),
textAlign = TextAlign.Center,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.fillMaxWidth()
)
}
@Composable
fun setChipDefaults(): ChipColors {
return ChipDefaults.primaryChipColors(
backgroundColor = colorResource(id = R.color.colorAccent),
contentColor = Color.Black
)
}
fun updateFavorites(entityViewModel: EntityViewModel, presenter: HomePresenter, mainScope: CoroutineScope) {
mainScope.launch { entityViewModel.favoriteEntities = presenter.getWearHomeFavorites().toMutableSet() }
}
fun saveFavorites(favorites: Set<String>, presenter: HomePresenter, mainScope: CoroutineScope) {
mainScope.launch { presenter.setWearHomeFavorites(favorites.toSet()) }
}
fun getIcon(icon: String?, domain: String, context: Context): IIcon? {
return if (icon?.startsWith("mdi") == true) {
val mdiIcon = icon.split(":")[1]
IconicsDrawable(context, "cmd-$mdiIcon").icon
} else {
when (domain) {
"input_boolean", "switch" -> CommunityMaterial.Icon2.cmd_light_switch
"light" -> CommunityMaterial.Icon2.cmd_lightbulb
"script" -> CommunityMaterial.Icon3.cmd_script_text_outline
"scene" -> CommunityMaterial.Icon3.cmd_palette_outline
else -> CommunityMaterial.Icon.cmd_cellphone
}
}
}

View file

@ -9,4 +9,5 @@ import io.homeassistant.companion.android.common.data.integration.Entity
class EntityViewModel : ViewModel() {
var entitiesResponse: Array<Entity<Any>> by mutableStateOf(arrayOf())
var favoriteEntities: MutableSet<String> by mutableStateOf(mutableSetOf())
}

View file

@ -34,4 +34,10 @@
<string name="input_booleans">Input Booleans</string>
<string name="switches">Switches</string>
<string name="loading_entities">Please wait while we load your entities</string>
<string name="favorite">Set Favorite Entities</string>
<string name="settings">Settings</string>
<string name="favorites">Favorites</string>
<string name="set_favorite">Set Favorites</string>
<string name="clear_favorites">Clear Favorites</string>
<string name="irreverisble">This action is irreversible</string>
</resources>