Skip to content

Commit

Permalink
Minor Assist UI feedback/changes (#3607)
Browse files Browse the repository at this point in the history
- Remove attribution
 - Scroll when messages are updated in case messages are longer than the "…" while running
 - Animation for active voice input
  • Loading branch information
jpelgrom committed Jun 26, 2023
1 parent 3c706bb commit 0279333
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,11 @@ class AssistViewModel @Inject constructor(
selectedPipeline =
allPipelines[selectedServerId]?.firstOrNull { it.id == id } ?: serverManager.webSocketRepository(selectedServerId).getAssistPipeline(id)
selectedPipeline?.let {
val attribution = serverManager.webSocketRepository(selectedServerId).getConversationAgentInfo(it.conversationEngine)?.attribution
currentPipeline = AssistUiPipeline(
serverId = selectedServerId,
serverName = serverManager.getServer(selectedServerId)?.friendlyName ?: "",
id = it.id,
name = it.name,
attributionName = attribution?.name,
attributionUrl = attribution?.url
name = it.name
)

_conversation.clear()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package io.homeassistant.companion.android.assist.ui

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
Expand All @@ -22,9 +29,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
Expand All @@ -35,6 +44,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.icons.Icons
Expand All @@ -51,19 +61,18 @@ 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.draw.scale
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.mikepenz.iconics.compose.Image
Expand Down Expand Up @@ -116,7 +125,7 @@ fun AssistSheetView(
) {
Column {
val lazyListState = rememberLazyListState()
LaunchedEffect(conversation.size) {
LaunchedEffect("${conversation.size}.${conversation.lastOrNull()?.message?.length}") {
lazyListState.animateScrollToItem(conversation.size)
}

Expand Down Expand Up @@ -169,13 +178,9 @@ fun AssistSheetHeader(
)
if (currentPipeline != null) {
val color = colorResource(commonR.color.colorOnSurfaceVariant)
val weight = if (currentPipeline.attributionName != null) 0.5f else 1f

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Box(Modifier.weight(weight, fill = false)) {
Row(modifier = Modifier.fillMaxWidth()) {
Box {
var pipelineShowList by remember { mutableStateOf(false) }
val pipelineShowServer by rememberSaveable(pipelines.size) {
mutableStateOf(pipelines.distinctBy { it.serverId }.size > 1)
Expand Down Expand Up @@ -216,22 +221,6 @@ fun AssistSheetHeader(
}
}
}
if (currentPipeline.attributionName != null) {
val uriHandler = LocalUriHandler.current
val baseModifier = Modifier.weight(weight, fill = false).padding(start = 8.dp)
val modifier = currentPipeline.attributionUrl?.let {
Modifier
.clickable { uriHandler.openUri(it) }
.then(baseModifier)
} ?: baseModifier
Text(
text = currentPipeline.attributionName,
textDecoration = if (currentPipeline.attributionUrl != null) TextDecoration.Underline else null,
color = color,
style = MaterialTheme.typography.caption,
modifier = modifier
)
}
}
}
}
Expand Down Expand Up @@ -303,22 +292,49 @@ fun AssistSheetControls(
} else {
Spacer(Modifier.size(48.dp))
Spacer(Modifier.weight(0.5f))
OutlinedButton({ onMicrophoneInput() }) {
Box(
modifier = Modifier.size(64.dp),
contentAlignment = Alignment.Center
) {
val inputIsActive = inputMode == AssistViewModel.AssistInputMode.VOICE_ACTIVE
Image(
asset = CommunityMaterial.Icon3.cmd_microphone,
contentDescription = stringResource(
if (inputIsActive) commonR.string.assist_stop_listening else commonR.string.assist_start_listening
),
colorFilter = ColorFilter.tint(
if (inputIsActive) {
LocalContentColor.current
} else {
MaterialTheme.colors.onSurface
}
),
modifier = Modifier.size(32.dp)
)
if (inputIsActive) {
val transition = rememberInfiniteTransition()
val scale by transition.animateFloat(
initialValue = 1f,
targetValue = 1.2f,
animationSpec = infiniteRepeatable(
animation = tween(600, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Surface(
color = colorResource(commonR.color.colorSpeechText),
modifier = Modifier.size(48.dp).scale(scale),
shape = CircleShape,
content = {}
)
}
OutlinedButton(
onClick = { onMicrophoneInput() },
modifier = Modifier.size(48.dp),
shape = CircleShape,
border = if (inputIsActive) null else ButtonDefaults.outlinedBorder,
colors = if (inputIsActive) {
ButtonDefaults.outlinedButtonColors(backgroundColor = Color.Transparent, contentColor = Color.Black)
} else {
ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colors.onSurface)
},
contentPadding = PaddingValues(all = 0.dp)
) {
Image(
asset = CommunityMaterial.Icon3.cmd_microphone,
contentDescription = stringResource(
if (inputIsActive) commonR.string.assist_stop_listening else commonR.string.assist_start_listening
),
colorFilter = ColorFilter.tint(LocalContentColor.current),
modifier = Modifier.size(28.dp)
)
}
}
Spacer(Modifier.weight(0.5f))
IconButton({ onChangeInput() }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ data class AssistUiPipeline(
val serverId: Int,
val serverName: String,
val id: String,
val name: String,
val attributionName: String? = null,
val attributionUrl: String? = null
val name: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.As
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineListResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.CompressedStateChangedEvent
import io.homeassistant.companion.android.common.data.websocket.impl.entities.ConversationAgentInfoResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.ConversationResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.CurrentUserResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.DeviceRegistryResponse
Expand Down Expand Up @@ -87,12 +86,6 @@ interface WebSocketRepository {
*/
suspend fun getConversation(speech: String): ConversationResponse?

/**
* Get information about the conversation agent.
* @param agentId Should be an [AssistPipelineResponse.conversationEngine]
*/
suspend fun getConversationAgentInfo(agentId: String): ConversationAgentInfoResponse?

/**
* Get information about an Assist pipeline.
* @param pipelineId the ID of the pipeline to get details for, if not specified the preferred
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.As
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineSttEnd
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineTtsEnd
import io.homeassistant.companion.android.common.data.websocket.impl.entities.CompressedStateChangedEvent
import io.homeassistant.companion.android.common.data.websocket.impl.entities.ConversationAgentInfoResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.ConversationResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.CurrentUserResponse
import io.homeassistant.companion.android.common.data.websocket.impl.entities.DeviceRegistryResponse
Expand Down Expand Up @@ -222,17 +221,6 @@ class WebSocketRepositoryImpl @AssistedInject constructor(
return mapResponse(socketResponse)
}

override suspend fun getConversationAgentInfo(agentId: String): ConversationAgentInfoResponse? {
val socketResponse = sendMessage(
mapOf(
"type" to "conversation/agent/info",
"agent_id" to agentId
)
)

return mapResponse(socketResponse)
}

override suspend fun getAssistPipeline(pipelineId: String?): AssistPipelineResponse? {
val data = mapOf(
"type" to "assist_pipeline/pipeline/get"
Expand Down

This file was deleted.

0 comments on commit 0279333

Please sign in to comment.