Fix Android voice bugs - STT restart and token retry
Critical Android fixes: BUG 1 - STT not restarting after TTS: - Problem: isSpeaking delay (300ms iOS visual) blocked Android STT - Android audio focus conflict: STT cannot start while isSpeaking=true - Fix: Platform-specific isSpeaking timing - iOS: 300ms delay (smooth visual indicator) - Android: immediate (allows STT to restart) BUG 2 - Session expired loop: - Problem: 401 error → token reset → no retry → user hears error - Fix: Automatic token refresh and retry on 401 - Flow: 401 → clear token → get new token → retry request - User never hears "Session expired" unless retry also fails contexts/VoiceContext.tsx:12-23,387-360
This commit is contained in:
parent
29fb3c1026
commit
8c0e36cae3
@ -17,6 +17,7 @@ import React, {
|
||||
useRef,
|
||||
ReactNode,
|
||||
} from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
import * as Speech from 'expo-speech';
|
||||
import { api } from '@/services/api';
|
||||
import { useVoiceTranscript } from './VoiceTranscriptContext';
|
||||
@ -322,10 +323,42 @@ export function VoiceProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
return responseText;
|
||||
} else {
|
||||
// Token might be expired
|
||||
// Token might be expired - retry with new token
|
||||
if (data.status === '401 Unauthorized') {
|
||||
console.log('[VoiceContext] Token expired, retrying with new token...');
|
||||
apiTokenRef.current = null;
|
||||
throw new Error('Session expired, please try again');
|
||||
|
||||
// Get new token and retry request
|
||||
const newToken = await getWellNuoToken();
|
||||
|
||||
const retryRequestParams: Record<string, string> = {
|
||||
function: voiceApiType,
|
||||
clientId: 'MA_001',
|
||||
user_name: WELLNUO_USER,
|
||||
token: newToken,
|
||||
question: normalizedQuestion,
|
||||
deployment_id: deploymentId,
|
||||
};
|
||||
|
||||
const retryResponse = await fetch(API_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams(retryRequestParams).toString(),
|
||||
signal: abortController.signal,
|
||||
});
|
||||
|
||||
const retryData = await retryResponse.json();
|
||||
|
||||
if (retryData.ok && retryData.response?.body) {
|
||||
const responseText = retryData.response.body;
|
||||
console.log('[VoiceContext] Retry succeeded:', responseText.slice(0, 100) + '...');
|
||||
setLastResponse(responseText);
|
||||
addTranscriptEntry('assistant', responseText);
|
||||
await speak(responseText);
|
||||
return responseText;
|
||||
} else {
|
||||
throw new Error(retryData.message || 'Could not get response after retry');
|
||||
}
|
||||
}
|
||||
throw new Error(data.message || 'Could not get response');
|
||||
}
|
||||
@ -400,11 +433,15 @@ export function VoiceProvider({ children }: { children: ReactNode }) {
|
||||
},
|
||||
onDone: () => {
|
||||
console.log('[VoiceContext] TTS completed');
|
||||
// Delay turning off green indicator to match STT restart delay (300ms)
|
||||
// This keeps the visual indicator on during the transition period
|
||||
// On iOS: Delay turning off green indicator to match STT restart delay (300ms)
|
||||
// On Android: Turn off immediately (audio focus conflict with STT)
|
||||
if (Platform.OS === 'ios') {
|
||||
setTimeout(() => {
|
||||
setIsSpeaking(false);
|
||||
}, 300);
|
||||
} else {
|
||||
setIsSpeaking(false);
|
||||
}
|
||||
// Return to listening state after speaking (if session wasn't stopped)
|
||||
if (!sessionStoppedRef.current) {
|
||||
setStatus('listening');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user