fix: dB-Range -85, Mute haert auch laufende TTS, VoIP-Anrufe + Bild-Bubble
Bug 1 — dB-Range erweitert: VAD_SILENCE_DB_MIN von -55 auf -85 dB. Damit hat Stefan einen weiten Regler-Spielraum wenn die adaptive Auto-Erkennung in seiner Umgebung nicht zuverlaessig greift. Bug 5 — Mute-Button stoppt laufende TTS nicht: audioService bekommt jetzt einen internen _muted-Flag. handlePcmChunk setzt silent automatisch wenn _muted true ist, playAudio kehrt frueh zurueck. Verhindert Race zwischen User-Klick auf Mute und einem TTS-Chunk der im selben JS-Tick ankommt (vorher: Ref-Update via useEffect erst nach dem Re-Render → Chunks "rutschten durch"). Plus ttsCanPlayRef wird im toggleMute-Handler synchron aktualisiert. Bug 4 — VoIP/Messenger-Anrufe erkennen: AudioFocusModule emittiert jetzt "AudioFocusChanged" Events mit type "loss"/"loss_transient"/"gain". WhatsApp/Signal/Discord/etc. requestn AudioFocus_GAIN_TRANSIENT_EXCLUSIVE wenn ein Anruf reinkommt — wir fangen das in phoneCall.ts ab und rufen halt + pauseForCall genau wie beim klassischen Anruf. Plus getMode() Polling-Fallback (alle 3s) weil GAIN nicht zuverlaessig kommt wenn wir den Focus selbst released haben — sobald AudioMode wieder NORMAL ist, resumeFromCall. Bug 6 — Bilder als "Strich": attachmentImage hatte width: '100%' in einer Bubble mit maxWidth: '80%' ohne explizite Parent-Breite → RN rendert auf 0px Breite. Neue ChatImage- Komponente nutzt Image.getSize um die echte aspectRatio zu messen + setzt sie dynamisch. Bubble passt sich dem Bild an. Bugs 2 (lange Texte mid-cutoff) + 3 (Spotify resumed) — brauchen ADB-Logs. ADB-WLAN ueber 192.168.177.22:5555 schlaegt fehl (refused) — bei Android 11+ braucht's Wireless-Debugging-Pairing-Code. Stefan kann den nennen sobald er soweit ist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,45 @@ const capMessages = (msgs: ChatMessage[]): ChatMessage[] =>
|
||||
const DEFAULT_ATTACHMENT_DIR = `${RNFS.DocumentDirectoryPath}/chat_attachments`;
|
||||
const STORAGE_PATH_KEY = 'aria_attachment_storage_path';
|
||||
|
||||
/** Image-Vorschau in der Chat-Bubble. Misst die echte Bild-Dimension via
|
||||
* Image.getSize + setzt aspectRatio dynamisch — dadurch passt sich die
|
||||
* Bubble ans Bild an (kein "Strich" mehr bei breiten oder hohen Bildern). */
|
||||
const CHAT_IMAGE_STYLE = {
|
||||
width: 260,
|
||||
borderRadius: 8,
|
||||
marginBottom: 6,
|
||||
backgroundColor: '#0D0D1A',
|
||||
} as const;
|
||||
const ChatImage: React.FC<{
|
||||
uri: string;
|
||||
onPress: () => void;
|
||||
onError: () => void;
|
||||
}> = ({ uri, onPress, onError }) => {
|
||||
const [aspectRatio, setAspectRatio] = useState<number>(4 / 3);
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
Image.getSize(uri, (w, h) => {
|
||||
if (!cancelled && w > 0 && h > 0) {
|
||||
// Aspect-Ratio capen damit sehr lange Panorama-Bilder oder hohe
|
||||
// Screenshot-Streifen die Bubble nicht sprengen
|
||||
const r = Math.max(0.5, Math.min(2.5, w / h));
|
||||
setAspectRatio(r);
|
||||
}
|
||||
}, () => {});
|
||||
return () => { cancelled = true; };
|
||||
}, [uri]);
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress} activeOpacity={0.8}>
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[CHAT_IMAGE_STYLE, { aspectRatio }]}
|
||||
resizeMode="cover"
|
||||
onError={onError}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
async function getAttachmentDir(): Promise<string> {
|
||||
try {
|
||||
const saved = await AsyncStorage.getItem(STORAGE_PATH_KEY);
|
||||
@@ -154,7 +193,9 @@ const ChatScreen: React.FC = () => {
|
||||
const enabled = await AsyncStorage.getItem('aria_tts_enabled');
|
||||
setTtsDeviceEnabled(enabled !== 'false'); // default true
|
||||
const muted = await AsyncStorage.getItem('aria_tts_muted');
|
||||
setTtsMuted(muted === 'true'); // default false
|
||||
const isMuted = muted === 'true';
|
||||
setTtsMuted(isMuted); // default false
|
||||
audioService.setMuted(isMuted); // service-internen Flag synchronisieren
|
||||
const voice = await AsyncStorage.getItem('aria_xtts_voice');
|
||||
localXttsVoiceRef.current = voice || '';
|
||||
ttsSpeedRef.current = await loadTtsSpeed();
|
||||
@@ -229,11 +270,15 @@ const ChatScreen: React.FC = () => {
|
||||
setTtsMuted(prev => {
|
||||
const next = !prev;
|
||||
AsyncStorage.setItem('aria_tts_muted', String(next));
|
||||
// Bei Muten sofort laufende Wiedergabe stoppen
|
||||
if (next) audioService.stopPlayback();
|
||||
// Ref synchron updaten — sonst kommen noch Chunks im selben Tick
|
||||
// mit canPlay=true durch (Race vor dem useEffect-Update).
|
||||
ttsCanPlayRef.current = ttsDeviceEnabled && !next;
|
||||
// Globalen Mute-Flag im audioService setzen — uebersteuert auch
|
||||
// payload.silent in handlePcmChunk und stoppt laufende Wiedergabe.
|
||||
audioService.setMuted(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
}, [ttsDeviceEnabled]);
|
||||
|
||||
// Chat-Verlauf aus AsyncStorage laden
|
||||
const isInitialLoad = useRef(true);
|
||||
@@ -925,11 +970,9 @@ const ChatScreen: React.FC = () => {
|
||||
{item.attachments?.map((att, idx) => (
|
||||
<View key={idx}>
|
||||
{att.type === 'image' && att.uri ? (
|
||||
<TouchableOpacity onPress={() => setFullscreenImage(att.uri || null)} activeOpacity={0.8}>
|
||||
<Image
|
||||
source={{ uri: att.uri }}
|
||||
style={styles.attachmentImage}
|
||||
resizeMode="cover"
|
||||
<ChatImage
|
||||
uri={att.uri}
|
||||
onPress={() => setFullscreenImage(att.uri || null)}
|
||||
onError={() => {
|
||||
setMessages(prev => prev.map(m =>
|
||||
m.id === item.id ? { ...m, attachments: m.attachments?.map((a, i) =>
|
||||
@@ -938,7 +981,6 @@ const ChatScreen: React.FC = () => {
|
||||
));
|
||||
}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : att.type === 'image' && !att.uri ? (
|
||||
<TouchableOpacity
|
||||
style={styles.attachmentFile}
|
||||
@@ -1341,9 +1383,11 @@ const styles = StyleSheet.create({
|
||||
color: '#E0E0F0',
|
||||
},
|
||||
attachmentImage: {
|
||||
width: '100%',
|
||||
minHeight: 200,
|
||||
maxHeight: 400,
|
||||
// Feste Breite + dynamische aspectRatio (in ChatImage gesetzt) damit die
|
||||
// Bubble sich ans Bild anpasst. Mit width: '100%' ohne explizite Parent-
|
||||
// Breite wuerde RN das Bild auf 0px schrumpfen → "Strich".
|
||||
width: 260,
|
||||
aspectRatio: 4 / 3,
|
||||
borderRadius: 8,
|
||||
marginBottom: 6,
|
||||
backgroundColor: '#0D0D1A',
|
||||
|
||||
Reference in New Issue
Block a user