mirror of
https://github.com/home-assistant/android
synced 2024-07-22 10:54:12 +00:00
Conversation tile design changes (#3233)
* Update conversation UI with speech bubble * Clean up design, vibrate when displaying response, close activity if screen turns off * Review comments * Switch response side to better match frontend * Design tweaks * Use row padding instead of spacer
This commit is contained in:
parent
33fc6c3779
commit
cce8b60c66
|
@ -32,4 +32,5 @@
|
|||
<color name="colorDeviceControlsDefaultOn">#8AB4F8</color>
|
||||
<color name="colorDeviceControlsThermostatHeat">#FF8B66</color>
|
||||
<color name="colorDeviceControlsCamera">#F1F3F4</color>
|
||||
<color name="colorSpeechText">#B3E5FC</color>
|
||||
</resources>
|
||||
|
|
|
@ -3,11 +3,13 @@ package io.homeassistant.companion.android.conversation
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.speech.RecognizerIntent
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.conversation.views.ConversationResultView
|
||||
|
@ -56,4 +58,12 @@ class ConversationActivity : ComponentActivity() {
|
|||
ConversationResultView(conversationViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
val pm = applicationContext.getSystemService<PowerManager>()
|
||||
if (pm?.isInteractive == false && conversationViewModel.conversationResult.isNotEmpty()) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ class ConversationViewModel @Inject constructor(
|
|||
var supportsConversation by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var isHapticEnabled = mutableStateOf(false)
|
||||
private set
|
||||
|
||||
fun getConversation() {
|
||||
viewModelScope.launch {
|
||||
conversationResult = integrationUseCase.getConversation(speechResult) ?: ""
|
||||
|
@ -38,6 +41,7 @@ class ConversationViewModel @Inject constructor(
|
|||
supportsConversation =
|
||||
integrationUseCase.isHomeAssistantVersionAtLeast(2023, 1, 0) &&
|
||||
webSocketRepository.getConfig()?.components?.contains("conversation") == true
|
||||
isHapticEnabled.value = integrationUseCase.getWearHapticFeedback()
|
||||
}
|
||||
|
||||
fun updateSpeechResult(result: String) {
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
package io.homeassistant.companion.android.conversation.views
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.wear.compose.material.PositionIndicator
|
||||
import androidx.wear.compose.material.Scaffold
|
||||
import androidx.wear.compose.material.ScalingLazyColumn
|
||||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.material.rememberScalingLazyListState
|
||||
import io.homeassistant.companion.android.common.R
|
||||
import io.homeassistant.companion.android.conversation.ConversationViewModel
|
||||
import io.homeassistant.companion.android.home.views.TimeText
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
|
||||
@Composable
|
||||
fun ConversationResultView(
|
||||
|
@ -30,28 +46,94 @@ fun ConversationResultView(
|
|||
},
|
||||
timeText = { TimeText(visible = !scrollState.isScrollInProgress) }
|
||||
) {
|
||||
ThemeLazyColumn(
|
||||
state = scrollState
|
||||
ScalingLazyColumn(
|
||||
state = scrollState,
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = conversationViewModel.speechResult.ifEmpty {
|
||||
if (conversationViewModel.supportsConversation)
|
||||
stringResource(R.string.no_results)
|
||||
else
|
||||
stringResource(R.string.no_conversation_support)
|
||||
},
|
||||
modifier = Modifier.padding(40.dp)
|
||||
)
|
||||
Column {
|
||||
Spacer(Modifier.padding(24.dp))
|
||||
SpeechBubble(
|
||||
text = conversationViewModel.speechResult.ifEmpty {
|
||||
if (conversationViewModel.supportsConversation)
|
||||
stringResource(R.string.no_results)
|
||||
else
|
||||
stringResource(R.string.no_conversation_support)
|
||||
},
|
||||
false
|
||||
)
|
||||
Spacer(Modifier.padding(8.dp))
|
||||
}
|
||||
}
|
||||
if (conversationViewModel.conversationResult.isNotEmpty())
|
||||
item {
|
||||
Text(
|
||||
if (conversationViewModel.isHapticEnabled.value) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
LaunchedEffect(key1 = "haptic") {
|
||||
haptic.performHapticFeedback(
|
||||
HapticFeedbackType.LongPress
|
||||
)
|
||||
}
|
||||
}
|
||||
SpeechBubble(
|
||||
text = conversationViewModel.conversationResult,
|
||||
modifier = Modifier.padding(top = 8.dp, start = 32.dp)
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpeechBubble(text: String, isResponse: Boolean) {
|
||||
Row(
|
||||
horizontalArrangement = if (isResponse) Arrangement.Start else Arrangement.End,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = if (isResponse) 0.dp else 24.dp,
|
||||
end = if (isResponse) 24.dp else 0.dp
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
if (isResponse)
|
||||
colorResource(R.color.colorAccent)
|
||||
else
|
||||
colorResource(R.color.colorSpeechText),
|
||||
AbsoluteRoundedCornerShape(
|
||||
topLeftPercent = 40,
|
||||
topRightPercent = 40,
|
||||
bottomLeftPercent = if (isResponse) 0 else 40,
|
||||
bottomRightPercent = if (isResponse) 40 else 0
|
||||
)
|
||||
)
|
||||
.padding(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = if (isResponse)
|
||||
Color.White
|
||||
else
|
||||
Color.Black,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = Devices.WEAR_OS_SMALL_ROUND)
|
||||
@Composable
|
||||
fun PreviewSpeechBubble() {
|
||||
ScalingLazyColumn(horizontalAlignment = Alignment.Start) {
|
||||
item {
|
||||
SpeechBubble(text = "Speech", isResponse = false)
|
||||
}
|
||||
item {
|
||||
SpeechBubble(text = "Response", isResponse = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue