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

Major Wear Cleanup #1895

Merged
merged 7 commits into from Nov 10, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Large diffs are not rendered by default.

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>)
}
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())
}
}
@@ -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()
}
}
@@ -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()
)
}
}
@@ -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)
}
}
}
}
}
}
}
}
@@ -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
)
}
}
}