Skip to content

Commit

Permalink
Major Wear Cleanup (#1895)
Browse files Browse the repository at this point in the history
* Better state hoisting.

* Broke down compose items and removed dependant state.

* Functional minus favorites....

* Favorites working, not my best solution.

* Breaking more stuff down.

* ktlint.
  • Loading branch information
JBassett authored Nov 10, 2021
1 parent eb3585b commit 4fdbe72
Show file tree
Hide file tree
Showing 14 changed files with 726 additions and 673 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import io.homeassistant.companion.android.common.data.integration.Entity
interface HomePresenter {

fun onViewReady()
fun onEntityClicked(entityId: String)
suspend fun onEntityClicked(entityId: String)
fun onLogoutClicked()
fun onFinish()
suspend fun getEntities(): List<Entity<Any>>
suspend fun getWearHomeFavorites(): Set<String>
suspend fun setWearHomeFavorites(favorites: Set<String>)
suspend fun getEntities(): List<Entity<*>>
suspend fun getWearHomeFavorites(): List<String>
suspend fun setWearHomeFavorites(favorites: List<String>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class HomePresenterImpl @Inject constructor(
}
}

override suspend fun getEntities(): List<Entity<Any>> {
override suspend fun getEntities(): List<Entity<*>> {
return try {
integrationUseCase.getEntities()
} catch (e: Exception) {
Expand All @@ -55,24 +55,20 @@ class HomePresenterImpl @Inject constructor(
}
}

override fun onEntityClicked(entityId: String) {
override suspend fun onEntityClicked(entityId: String) {

if (entityId.split(".")[0] in toggleDomains) {
mainScope.launch {
integrationUseCase.callService(
entityId.split(".")[0],
"toggle",
hashMapOf("entity_id" to entityId)
)
}
integrationUseCase.callService(
entityId.split(".")[0],
"toggle",
hashMapOf("entity_id" to entityId)
)
} else {
mainScope.launch {
integrationUseCase.callService(
entityId.split(".")[0],
"turn_on",
hashMapOf("entity_id" to entityId)
)
}
integrationUseCase.callService(
entityId.split(".")[0],
"turn_on",
hashMapOf("entity_id" to entityId)
)
}
}

Expand Down Expand Up @@ -103,11 +99,11 @@ class HomePresenterImpl @Inject constructor(
}
}

override suspend fun getWearHomeFavorites(): Set<String> {
return integrationUseCase.getWearHomeFavorites()
override suspend fun getWearHomeFavorites(): List<String> {
return integrationUseCase.getWearHomeFavorites().toList()
}

override suspend fun setWearHomeFavorites(favorites: Set<String>) {
integrationUseCase.setWearHomeFavorites(favorites)
override suspend fun setWearHomeFavorites(favorites: List<String>) {
integrationUseCase.setWearHomeFavorites(favorites.toSet())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.homeassistant.companion.android.home

import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.homeassistant.companion.android.common.data.integration.Entity
import kotlinx.coroutines.launch

class MainViewModel : ViewModel() {

private lateinit var homePresenter: HomePresenter

// TODO: This is bad, do this instead: https://stackoverflow.com/questions/46283981/android-viewmodel-additional-arguments
fun init(homePresenter: HomePresenter) {
this.homePresenter = homePresenter
loadEntities()
}

var entities = mutableStateListOf<Entity<*>>()
private set
var favoriteEntityIds = mutableStateListOf<String>()
private set

fun loadEntities() {
viewModelScope.launch {
entities.addAll(homePresenter.getEntities())
favoriteEntityIds.addAll(homePresenter.getWearHomeFavorites())
}
}

fun toggleEntity(entityId: String) {
viewModelScope.launch {
homePresenter.onEntityClicked(entityId)
val updatedEntities = homePresenter.getEntities()
// This should be better....
for (i in updatedEntities.indices) {
entities[i] = updatedEntities[i]
}
}
}

fun addFavorite(entityId: String) {

viewModelScope.launch {
favoriteEntityIds.add(entityId)
homePresenter.setWearHomeFavorites(favoriteEntityIds)
}
}

fun removeFavorite(entity: String) {

viewModelScope.launch {
favoriteEntityIds.remove(entity)
homePresenter.setWearHomeFavorites(favoriteEntityIds)
}
}

fun clearFavorites() {
viewModelScope.launch {
favoriteEntityIds.clear()
homePresenter.setWearHomeFavorites(favoriteEntityIds)
}
}

fun logout() {
homePresenter.onLogoutClicked()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.homeassistant.companion.android.home.views

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.res.colorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.Text
import androidx.wear.compose.material.ToggleChip
import androidx.wear.compose.material.ToggleChipDefaults
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.HomePresenterImpl
import io.homeassistant.companion.android.util.getIcon
import io.homeassistant.companion.android.util.setChipDefaults

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

if (entity.entityId.split(".")[0] in HomePresenterImpl.toggleDomains) {
ToggleChip(
checked = entity.state == "on",
onCheckedChange = { onEntityClicked(entity.entityId) },
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
appIcon = { Image(asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone) },
label = {
Text(
text = attributes["friendly_name"].toString(),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
enabled = entity.state != "unavailable",
toggleIcon = { ToggleChipDefaults.SwitchIcon(entity.state == "on") },
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
)
)
} else {
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
icon = { Image(asset = iconBitmap ?: CommunityMaterial.Icon.cmd_cellphone) },
label = {
Text(
text = attributes["friendly_name"].toString(),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
},
enabled = entity.state != "unavailable",
onClick = { onEntityClicked(entity.entityId) },
colors = setChipDefaults()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.homeassistant.companion.android.home.views

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
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.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ExperimentalWearMaterialApi
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.home.HomePresenterImpl
import io.homeassistant.companion.android.home.MainViewModel
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.SetTitle
import io.homeassistant.companion.android.util.setChipDefaults

private const val SCREEN_LANDING = "landing"
private const val SCREEN_SETTINGS = "settings"
private const val SCREEN_SET_FAVORITES = "set_favorites"

@ExperimentalWearMaterialApi
@Composable
fun LoadHomePage(
mainViewModel: MainViewModel
) {

val rotaryEventDispatcher = RotaryEventDispatcher()
if (mainViewModel.entities.isNullOrEmpty() && mainViewModel.favoriteEntityIds.isNullOrEmpty()) {
Column {
Spacer(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp)
)
SetTitle(id = R.string.loading)
Chip(
modifier = Modifier
.padding(top = 50.dp, start = 10.dp, end = 10.dp),
label = {
Text(
text = stringResource(R.string.loading_entities),
textAlign = TextAlign.Center
)
},
onClick = { /* No op */ },
colors = setChipDefaults()
)
}
} else {
val swipeDismissableNavController = rememberSwipeDismissableNavController()
MaterialTheme {
CompositionLocalProvider(
LocalRotaryEventDispatcher provides rotaryEventDispatcher
) {
RotaryEventHandlerSetup(rotaryEventDispatcher)
SwipeDismissableNavHost(
navController = swipeDismissableNavController,
startDestination = SCREEN_LANDING
) {
composable(SCREEN_LANDING) {
MainView(
mainViewModel.entities,
mainViewModel.favoriteEntityIds,
{ mainViewModel.toggleEntity(it) },
{ swipeDismissableNavController.navigate(SCREEN_SETTINGS) },
{ mainViewModel.logout() }
)
}
composable(SCREEN_SETTINGS) {
SettingsView(
mainViewModel.favoriteEntityIds,
{ swipeDismissableNavController.navigate(SCREEN_SET_FAVORITES) },
{ mainViewModel.clearFavorites() }
)
}
composable(SCREEN_SET_FAVORITES) {
val validEntities = mainViewModel.entities
.filter { it.entityId.split(".")[0] in HomePresenterImpl.supportedDomains }
SetFavoritesView(
validEntities,
mainViewModel.favoriteEntityIds
) { entityId, isSelected ->
if (isSelected) {
mainViewModel.addFavorite(entityId)
} else {
mainViewModel.removeFavorite(entityId)
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.homeassistant.companion.android.home.views

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.wear.compose.material.ListHeader
import androidx.wear.compose.material.Text

@Composable
fun ListHeader(
stringId: Int,
expanded: Boolean,
onExpandChanged: (Boolean) -> Unit
) {
ListHeader(
modifier = Modifier
.clickable { onExpandChanged(!expanded) }
) {
Row {
Text(
text = stringResource(id = stringId) + if (expanded) " -" else " +",
color = Color.White
)
}
}
}
Loading

0 comments on commit 4fdbe72

Please sign in to comment.