feat(android): add haptic feedback for chat messages

- Add haptic feedback (LongPress) when sending a message
- Add subtle haptic feedback (TextHandleMove) when a new assistant message arrives

Improves tactile UX for chat interactions.
This commit is contained in:
JARVIS 2026-01-27 20:05:18 +01:00
parent 3fe4b2595a
commit 6f473c341f
2 changed files with 23 additions and 0 deletions

View File

@ -37,6 +37,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.clawdbot.android.chat.ChatSessionEntry import com.clawdbot.android.chat.ChatSessionEntry
@ -61,6 +63,7 @@ fun ChatComposer(
var input by rememberSaveable { mutableStateOf("") } var input by rememberSaveable { mutableStateOf("") }
var showThinkingMenu by remember { mutableStateOf(false) } var showThinkingMenu by remember { mutableStateOf(false) }
var showSessionMenu by remember { mutableStateOf(false) } var showSessionMenu by remember { mutableStateOf(false) }
val haptic = LocalHapticFeedback.current
val sessionOptions = resolveSessionChoices(sessionKey, sessions, mainSessionKey = mainSessionKey) val sessionOptions = resolveSessionChoices(sessionKey, sessions, mainSessionKey = mainSessionKey)
val currentSessionLabel = val currentSessionLabel =
@ -165,6 +168,7 @@ fun ChatComposer(
} }
} else { } else {
FilledTonalIconButton(onClick = { FilledTonalIconButton(onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
val text = input val text = input
input = "" input = ""
onSend(text) onSend(text)

View File

@ -16,9 +16,15 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.clawdbot.android.chat.ChatMessage import com.clawdbot.android.chat.ChatMessage
import com.clawdbot.android.chat.ChatPendingToolCall import com.clawdbot.android.chat.ChatPendingToolCall
@ -32,6 +38,19 @@ fun ChatMessageListCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val listState = rememberLazyListState() val listState = rememberLazyListState()
val haptic = LocalHapticFeedback.current
var previousMessageCount by remember { mutableIntStateOf(messages.size) }
// Haptic feedback when a new assistant message arrives
LaunchedEffect(messages.size) {
if (messages.size > previousMessageCount) {
val lastMessage = messages.lastOrNull()
if (lastMessage != null && lastMessage.role.lowercase() == "assistant") {
haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
}
previousMessageCount = messages.size
}
LaunchedEffect(messages.size, pendingRunCount, pendingToolCalls.size, streamingAssistantText) { LaunchedEffect(messages.size, pendingRunCount, pendingToolCalls.size, streamingAssistantText) {
val total = val total =