From 6f473c341f612df7491c67ced09943116975d5ac Mon Sep 17 00:00:00 2001 From: JARVIS Date: Tue, 27 Jan 2026 20:05:18 +0100 Subject: [PATCH] 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. --- .../clawdbot/android/ui/chat/ChatComposer.kt | 4 ++++ .../android/ui/chat/ChatMessageListCard.kt | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt b/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt index 1f30938e0..d14922eb3 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatComposer.kt @@ -37,6 +37,8 @@ import androidx.compose.runtime.setValue 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.unit.dp import com.clawdbot.android.chat.ChatSessionEntry @@ -61,6 +63,7 @@ fun ChatComposer( var input by rememberSaveable { mutableStateOf("") } var showThinkingMenu by remember { mutableStateOf(false) } var showSessionMenu by remember { mutableStateOf(false) } + val haptic = LocalHapticFeedback.current val sessionOptions = resolveSessionChoices(sessionKey, sessions, mainSessionKey = mainSessionKey) val currentSessionLabel = @@ -165,6 +168,7 @@ fun ChatComposer( } } else { FilledTonalIconButton(onClick = { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) val text = input input = "" onSend(text) diff --git a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt b/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt index a3229d4a2..bea3c2ee5 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/ui/chat/ChatMessageListCard.kt @@ -16,9 +16,15 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.Modifier 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 com.clawdbot.android.chat.ChatMessage import com.clawdbot.android.chat.ChatPendingToolCall @@ -32,6 +38,19 @@ fun ChatMessageListCard( modifier: Modifier = Modifier, ) { 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) { val total =