diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt index 146be9d7b..6c63f3a1e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt @@ -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() diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt index 534901ab9..0be573b7e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt @@ -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 @@ -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 @@ -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 @@ -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 @@ -116,7 +125,7 @@ fun AssistSheetView( ) { Column { val lazyListState = rememberLazyListState() - LaunchedEffect(conversation.size) { + LaunchedEffect("${conversation.size}.${conversation.lastOrNull()?.message?.length}") { lazyListState.animateScrollToItem(conversation.size) } @@ -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) @@ -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 - ) - } } } } @@ -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() }) { diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistUiPipeline.kt b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistUiPipeline.kt index 526d7c728..2eaa9b5a1 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistUiPipeline.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistUiPipeline.kt @@ -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 ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt index 410293cc0..5fd15d7e4 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt @@ -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 @@ -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 diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt index 7fa88abbb..a9c5e64ed 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt @@ -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 @@ -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" diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ConversationAgentInfoResponse.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ConversationAgentInfoResponse.kt deleted file mode 100644 index 13ddeb681..000000000 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ConversationAgentInfoResponse.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.homeassistant.companion.android.common.data.websocket.impl.entities - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties - -@JsonIgnoreProperties(ignoreUnknown = true) -data class ConversationAgentInfoResponse( - val attribution: ConversationAgentAttribution? -) - -@JsonIgnoreProperties(ignoreUnknown = true) -data class ConversationAgentAttribution( - val name: String, - val url: String? -)