Skip to content

Commit

Permalink
Cleanup: camera entities selection, empty state, preview image
Browse files Browse the repository at this point in the history
 - On initial load also create a list of camera entities to make it possible to select them without showing up elsewhere in the app
 - Add text to empty state instructing the user to set a camera
 - Update tile preview images
  • Loading branch information
jpelgrom committed Sep 15, 2023
1 parent 95692de commit cdd8a31
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 55 deletions.
1 change: 1 addition & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
<string name="camera_tile_desc">See what\'s on your camera</string>
<string name="camera_tile_log_in">Log in to Home Assistant to add a camera tile</string>
<string name="camera_tile_no_tiles_yet">There are no camera tiles added yet - add one from the watch face to set it up</string>
<string name="camera_tile_no_entity_yet">Edit the tile settings and select a camera to show</string>
<string name="camera_tile_n">Camera tile #%d</string>
<string name="camera_tiles">Camera tiles</string>
<string name="camera_widgets">Camera widgets</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class MainViewModel @Inject constructor(
val shortcutEntitiesMap = mutableStateMapOf<Int?, SnapshotStateList<SimplifiedEntity>>()

val cameraTiles = cameraTileDao.getAllFlow().collectAsState()
var cameraEntitiesMap = mutableStateMapOf<String, SnapshotStateList<Entity<*>>>()
private set

var areas = mutableListOf<AreaRegistryResponse>()
private set
Expand Down Expand Up @@ -226,6 +228,10 @@ class MainViewModel @Inject constructor(
getEntities.await()?.also {
entities.clear()
it.forEach { state -> updateEntityStates(state) }

// Special list: camera entities
val cameraEntities = it.filter { entity -> entity.domain == "camera" }
cameraEntitiesMap["camera"] = mutableStateListOf<Entity<*>>().apply { addAll(cameraEntities) }
}
if (!isFavoritesOnly) {
updateEntityDomains()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.homeassistant.companion.android.home.views

import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -200,6 +199,7 @@ fun LoadHomePage(
val tileId = backStackEntry.arguments?.getInt(ARG_SCREEN_CAMERA_TILE_ID)
SetCameraTileView(
tile = mainViewModel.cameraTiles.value.firstOrNull { it.id == tileId },
entities = mainViewModel.cameraEntitiesMap["camera"],
onSelectEntity = {
swipeDismissableNavController.navigate("$ROUTE_CAMERA_TILE/$tileId/$SCREEN_SET_CAMERA_TILE_ENTITY")
},
Expand All @@ -221,7 +221,7 @@ fun LoadHomePage(
val cameraFavorites = remember { mutableStateOf(emptyList<String>()) } // There are no camera favorites
ChooseEntityView(
entitiesByDomainOrder = cameraDomains,
entitiesByDomain = mainViewModel.entitiesByDomain,
entitiesByDomain = mainViewModel.cameraEntitiesMap,
favoriteEntityIds = cameraFavorites,
onNoneClicked = {},
onEntitySelected = { entity ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import androidx.wear.compose.material.Text
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.common.R
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.friendlyName
import io.homeassistant.companion.android.common.data.integration.getIcon
import io.homeassistant.companion.android.database.wear.CameraTile
import io.homeassistant.companion.android.theme.WearAppTheme
import io.homeassistant.companion.android.theme.wearColorPalette
Expand All @@ -27,6 +30,7 @@ import io.homeassistant.companion.android.common.R as commonR
@Composable
fun SetCameraTileView(
tile: CameraTile?,
entities: List<Entity<*>>?,
onSelectEntity: () -> Unit,
onSelectRefreshInterval: () -> Unit
) {
Expand All @@ -45,11 +49,15 @@ fun SetCameraTileView(
ListHeader(commonR.string.camera_tile)
}
item {
val entity = tile?.entityId?.let { tileEntityId ->
entities?.firstOrNull { it.entityId == tileEntityId }
}
val icon = entity?.getIcon(LocalContext.current) ?: CommunityMaterial.Icon3.cmd_video
Chip(
modifier = Modifier.fillMaxWidth(),
icon = {
Image(
asset = CommunityMaterial.Icon3.cmd_video, // TODO
asset = icon,
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
)
},
Expand All @@ -60,7 +68,7 @@ fun SetCameraTileView(
)
},
secondaryLabel = {
Text(tile?.entityId ?: "")
Text(entity?.friendlyName ?: tile?.entityId ?: "")
},
onClick = onSelectEntity
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,8 @@ class CameraTile : TileService() {
if (serverManager.isRegistered() && tileConfig != null) {
timeline(
requestParams.deviceConfiguration.screenWidthDp,
requestParams.deviceConfiguration.screenHeightDp
)
} else if (serverManager.isRegistered()) {
// TODO emptystate
timeline(
requestParams.deviceConfiguration.screenWidthDp,
requestParams.deviceConfiguration.screenHeightDp
requestParams.deviceConfiguration.screenHeightDp,
tileConfig.entityId.isNullOrBlank()
)
} else {
loggedOutTimeline(
Expand Down Expand Up @@ -192,17 +187,26 @@ class CameraTile : TileService() {
serviceScope.cancel()
}

private fun timeline(width: Int, height: Int): Timeline = Timeline.fromLayoutElement(
private fun timeline(width: Int, height: Int, requiresSetup: Boolean): Timeline = Timeline.fromLayoutElement(
LayoutElementBuilders.Box.Builder().apply {
// Camera image
addContent(
LayoutElementBuilders.Image.Builder()
.setResourceId(RESOURCE_SNAPSHOT)
.setWidth(DimensionBuilders.dp(width.toFloat()))
.setHeight(DimensionBuilders.dp(height.toFloat()))
.setContentScaleMode(CONTENT_SCALE_MODE_FIT)
.build()
)
if (requiresSetup) {
addContent(
LayoutElementBuilders.Text.Builder()
.setText(getString(commonR.string.camera_tile_no_entity_yet))
.setMaxLines(10)
.build()
)
} else {
addContent(
LayoutElementBuilders.Image.Builder()
.setResourceId(RESOURCE_SNAPSHOT)
.setWidth(DimensionBuilders.dp(width.toFloat()))
.setHeight(DimensionBuilders.dp(height.toFloat()))
.setContentScaleMode(CONTENT_SCALE_MODE_FIT)
.build()
)
}
// Refresh button
addContent(getRefreshButton())
// Click: refresh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,31 @@ fun loggedOutTimeline(
requestParams: RequestBuilders.TileRequest,
@StringRes title: Int,
@StringRes text: Int
): Timeline = primaryLayoutTimeline(
context = context,
requestParams = requestParams,
title = title,
text = text,
actionText = commonR.string.login,
action = ActionBuilders.LaunchAction.Builder()
.setAndroidActivity(
ActionBuilders.AndroidActivity.Builder()
.setClassName(SplashActivity::class.java.name)
.setPackageName(context.packageName)
.build()
).build()
)

/**
* A [Timeline] with a single entry using the Material `PrimaryLayout`. The title is optional.
*/
fun primaryLayoutTimeline(
context: Context,
requestParams: RequestBuilders.TileRequest,
@StringRes title: Int?,
@StringRes text: Int,
@StringRes actionText: Int,
action: ActionBuilders.Action
): Timeline {
val theme = Colors(
ContextCompat.getColor(context, R.color.colorPrimary), // Primary
Expand All @@ -59,43 +84,36 @@ fun loggedOutTimeline(
)
val chipColors = ChipColors.primaryChipColors(theme)
val chipAction = ModifiersBuilders.Clickable.Builder()
.setId("login")
.setOnClick(
ActionBuilders.LaunchAction.Builder()
.setAndroidActivity(
ActionBuilders.AndroidActivity.Builder()
.setClassName(SplashActivity::class.java.name)
.setPackageName(context.packageName)
.build()
).build()
).build()
return Timeline.fromLayoutElement(
PrimaryLayout.Builder(requestParams.deviceConfiguration)
.setPrimaryLabelTextContent(
Text.Builder(context, context.getString(title))
.setTypography(Typography.TYPOGRAPHY_CAPTION1)
.setColor(argb(theme.primary))
.build()
)
.setContent(
Text.Builder(context, context.getString(text))
.setTypography(Typography.TYPOGRAPHY_BODY1)
.setMaxLines(10)
.setColor(argb(theme.onSurface))
.build()
)
.setPrimaryChipContent(
CompactChip.Builder(
context,
context.getString(commonR.string.login),
chipAction,
requestParams.deviceConfiguration
)
.setChipColors(chipColors)
.build()
)
.setId("action")
.setOnClick(action)
.build()
val builder = PrimaryLayout.Builder(requestParams.deviceConfiguration)
if (title != null) {
builder.setPrimaryLabelTextContent(
Text.Builder(context, context.getString(title))
.setTypography(Typography.TYPOGRAPHY_CAPTION1)
.setColor(argb(theme.primary))
.build()
)
}
builder.setContent(
Text.Builder(context, context.getString(text))
.setTypography(Typography.TYPOGRAPHY_BODY1)
.setMaxLines(10)
.setColor(argb(theme.onSurface))
.build()
)
builder.setPrimaryChipContent(
CompactChip.Builder(
context,
context.getString(actionText),
chipAction,
requestParams.deviceConfiguration
)
.setChipColors(chipColors)
.build()
)
return Timeline.fromLayoutElement(builder.build())
}

/**
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed wear/src/main/res/drawable/camera_tile_example.webp
Binary file not shown.

0 comments on commit cdd8a31

Please sign in to comment.