Android: tap orb to dismiss talk mode with haptic feedback

Makes the talk orb tappable to disable voice mode - much easier to hit
than the small button. Includes haptic feedback on tap.
This commit is contained in:
yishai 2026-01-28 21:31:46 +02:00
parent e5198b8f31
commit 32cf8c3290
2 changed files with 21 additions and 1 deletions

View File

@ -271,6 +271,7 @@ fun RootScreen(viewModel: MainViewModel) {
statusText = talkStatusText, statusText = talkStatusText,
isListening = talkIsListening, isListening = talkIsListening,
isSpeaking = talkIsSpeaking, isSpeaking = talkIsSpeaking,
onDismiss = { viewModel.setTalkEnabled(false) },
) )
} }
} }

View File

@ -7,6 +7,8 @@ import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -18,11 +20,14 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
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.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -33,7 +38,9 @@ fun TalkOrbOverlay(
isListening: Boolean, isListening: Boolean,
isSpeaking: Boolean, isSpeaking: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onDismiss: (() -> Unit)? = null,
) { ) {
val haptic = LocalHapticFeedback.current
val transition = rememberInfiniteTransition(label = "talk-orb") val transition = rememberInfiniteTransition(label = "talk-orb")
val t by val t by
transition.animateFloat( transition.animateFloat(
@ -62,7 +69,19 @@ fun TalkOrbOverlay(
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Box(contentAlignment = Alignment.Center) { Box(contentAlignment = Alignment.Center) {
Canvas(modifier = Modifier.size(360.dp)) { Canvas(
modifier = Modifier
.size(360.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null, // No ripple - the orb itself is the feedback
enabled = onDismiss != null,
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
onDismiss?.invoke()
}
)
) {
val center = this.center val center = this.center
val baseRadius = size.minDimension * 0.30f val baseRadius = size.minDimension * 0.30f