mirror of
https://github.com/home-assistant/android
synced 2024-10-02 22:34:46 +00:00
Faster entity selection Wear OS (#2804)
* Always collapse headers and add favorites section * Remove unneeded entity update subscription * Process comments * Use shorthand for favorites loop in MainView
This commit is contained in:
parent
a995d9410b
commit
d00c823651
|
@ -109,6 +109,7 @@
|
|||
<string name="choose_server">Choose server</string>
|
||||
<string name="clear_favorites">Clear Favorites</string>
|
||||
<string name="color_temp">Color temperature: %1$d</string>
|
||||
<string name="collapse">Collapse</string>
|
||||
<string name="complication_entity_invalid">Invalid entity</string>
|
||||
<string name="complication_entity_state_content_description">Entity state</string>
|
||||
<string name="complication_entity_state_label">Entity state</string>
|
||||
|
@ -201,6 +202,7 @@
|
|||
<string name="error_http_generic">There was an error loading Home Assistant. Please review the connection settings and try again.\n\nError Code: %d \nDescription: %s</string>
|
||||
<string name="event_error">Failed to notify HA of picked option.</string>
|
||||
<string name="exit">Exit</string>
|
||||
<string name="expand">Expand</string>
|
||||
<string name="failed_authentication">Could not authenticate</string>
|
||||
<string name="failed_connection">Could not connect</string>
|
||||
<string name="failed_registration">Could not register</string>
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.homeassistant.companion.android.complications
|
|||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
|
@ -20,12 +21,16 @@ import io.homeassistant.companion.android.common.data.websocket.WebSocketState
|
|||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplications
|
||||
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
|
||||
import io.homeassistant.companion.android.database.wear.FavoritesDao
|
||||
import io.homeassistant.companion.android.database.wear.getAllFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ComplicationConfigViewModel @Inject constructor(
|
||||
application: Application,
|
||||
private val favoritesDao: FavoritesDao,
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val webSocketUseCase: WebSocketRepository,
|
||||
private val entityStateComplicationsDao: EntityStateComplicationsDao
|
||||
|
@ -46,6 +51,7 @@ class ComplicationConfigViewModel @Inject constructor(
|
|||
private set
|
||||
var entitiesByDomainOrder = mutableStateListOf<String>()
|
||||
private set
|
||||
val favoriteEntityIds = favoritesDao.getAllFlow().collectAsState()
|
||||
|
||||
var loadingState by mutableStateOf(LoadingState.LOADING)
|
||||
private set
|
||||
|
@ -81,14 +87,6 @@ class ComplicationConfigViewModel @Inject constructor(
|
|||
} else {
|
||||
LoadingState.ERROR
|
||||
}
|
||||
|
||||
// Listen for updates
|
||||
viewModelScope.launch {
|
||||
integrationUseCase.getEntityUpdates()?.collect {
|
||||
entities[it.entityId] = it
|
||||
updateEntityDomains()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception while loading entities", e)
|
||||
loadingState = LoadingState.ERROR
|
||||
|
@ -124,4 +122,18 @@ class ComplicationConfigViewModel @Inject constructor(
|
|||
entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Flow into a State object that updates until the view model is cleared.
|
||||
*/
|
||||
private fun <T> Flow<T>.collectAsState(
|
||||
initial: T
|
||||
): State<T> {
|
||||
val state = mutableStateOf(initial)
|
||||
viewModelScope.launch {
|
||||
collect { state.value = it }
|
||||
}
|
||||
return state
|
||||
}
|
||||
private fun <T> Flow<List<T>>.collectAsState(): State<List<T>> = collectAsState(initial = emptyList())
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ fun LoadConfigView(
|
|||
ChooseEntityView(
|
||||
entitiesByDomainOrder = complicationConfigViewModel.entitiesByDomainOrder,
|
||||
entitiesByDomain = complicationConfigViewModel.entitiesByDomain,
|
||||
favoriteEntityIds = complicationConfigViewModel.favoriteEntityIds,
|
||||
onNoneClicked = {},
|
||||
onEntitySelected = { entity ->
|
||||
complicationConfigViewModel.setEntity(entity)
|
||||
|
|
|
@ -171,6 +171,7 @@ fun LoadHomePage(
|
|||
ChooseEntityView(
|
||||
entitiesByDomainOrder = mainViewModel.entitiesByDomainOrder,
|
||||
entitiesByDomain = mainViewModel.entitiesByDomain,
|
||||
favoriteEntityIds = mainViewModel.favoriteEntityIds,
|
||||
onNoneClicked = {
|
||||
mainViewModel.clearTileShortcut(shortcutEntitySelectionIndex)
|
||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
||||
|
|
|
@ -111,21 +111,16 @@ fun MainView(
|
|||
colors = ChipDefaults.secondaryChipColors()
|
||||
)
|
||||
} else {
|
||||
var isValidEntity = false
|
||||
for (entity in mainViewModel.entities) {
|
||||
if (entity.value.entityId == favoriteEntityID) {
|
||||
isValidEntity = true
|
||||
mainViewModel.entities.values.toList()
|
||||
.firstOrNull { it.entityId == favoriteEntityID }
|
||||
?.let {
|
||||
EntityUi(
|
||||
mainViewModel.entities[favoriteEntityID]!!,
|
||||
onEntityClicked,
|
||||
isHapticEnabled,
|
||||
isToastEnabled
|
||||
) { entityId -> onEntityLongClicked(entityId) }
|
||||
}
|
||||
}
|
||||
if (!isValidEntity) {
|
||||
deleteFavorite(favoriteEntityID)
|
||||
}
|
||||
} ?: deleteFavorite(favoriteEntityID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@ package io.homeassistant.companion.android.views
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -32,12 +37,14 @@ import io.homeassistant.companion.android.common.R as commonR
|
|||
fun ChooseEntityView(
|
||||
entitiesByDomainOrder: SnapshotStateList<String>,
|
||||
entitiesByDomain: SnapshotStateMap<String, SnapshotStateList<Entity<*>>>,
|
||||
favoriteEntityIds: State<List<String>>,
|
||||
onNoneClicked: () -> Unit,
|
||||
onEntitySelected: (entity: SimplifiedEntity) -> Unit,
|
||||
allowNone: Boolean = true
|
||||
) {
|
||||
// Remember expanded state of each header
|
||||
val expandedStates = rememberExpandedStates(entitiesByDomainOrder)
|
||||
var expandedFavorites: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
WearAppTheme {
|
||||
ThemeLazyColumn {
|
||||
|
@ -59,6 +66,30 @@ fun ChooseEntityView(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (favoriteEntityIds.value.isNotEmpty()) {
|
||||
item {
|
||||
ExpandableListHeader(
|
||||
string = stringResource(commonR.string.favorites),
|
||||
expanded = expandedFavorites,
|
||||
onExpandChanged = { expandedFavorites = it }
|
||||
)
|
||||
}
|
||||
if (expandedFavorites) {
|
||||
items(favoriteEntityIds.value.size) { index ->
|
||||
val favoriteEntityID = favoriteEntityIds.value[index].split(",")[0]
|
||||
entitiesByDomain.flatMap { (_, values) -> values }
|
||||
.firstOrNull { it.entityId == favoriteEntityID }
|
||||
?.let {
|
||||
ChooseEntityChip(
|
||||
entity = it,
|
||||
onEntitySelected = onEntitySelected
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (domain in entitiesByDomainOrder) {
|
||||
val entities = entitiesByDomain[domain]
|
||||
if (!entities.isNullOrEmpty()) {
|
||||
|
|
|
@ -2,16 +2,24 @@ package io.homeassistant.companion.android.views
|
|||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.wear.compose.material.Icon
|
||||
import androidx.wear.compose.material.LocalContentColor
|
||||
import androidx.wear.compose.material.Text
|
||||
import io.homeassistant.companion.android.common.R
|
||||
import com.mikepenz.iconics.compose.Image
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import org.checkerframework.checker.units.qual.K
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
/**
|
||||
* Remember expanded state of each header
|
||||
|
@ -23,7 +31,7 @@ fun <K> rememberExpandedStates(
|
|||
return remember {
|
||||
mutableStateMapOf<K, Boolean>().apply {
|
||||
initialKeys.forEach { key ->
|
||||
put(key, true)
|
||||
put(key, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +48,14 @@ fun ExpandableListHeader(
|
|||
.clickable { onExpandChanged(!expanded) }
|
||||
) {
|
||||
Row {
|
||||
val plusMinus = if (expanded) "-" else "+"
|
||||
Text(
|
||||
text = "$string\u2001$plusMinus"
|
||||
text = string
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Image(
|
||||
asset = if (expanded) CommunityMaterial.Icon.cmd_chevron_up else CommunityMaterial.Icon.cmd_chevron_down,
|
||||
contentDescription = stringResource(if (expanded) commonR.string.collapse else commonR.string.expand),
|
||||
colorFilter = ColorFilter.tint(LocalContentColor.current)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +78,7 @@ fun <K> ExpandableListHeader(
|
|||
@Composable
|
||||
private fun PreviewExpandableListHeader() {
|
||||
ExpandableListHeader(
|
||||
string = stringResource(R.string.other),
|
||||
string = stringResource(commonR.string.other),
|
||||
expanded = true,
|
||||
onExpandChanged = {}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue