Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2edee9adb | |||
| bb13477ef9 | |||
| 710e7c88d8 | |||
| b6ee5552f0 |
@@ -79,8 +79,8 @@ android {
|
||||
applicationId "com.ariacockpit"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 10806
|
||||
versionName "0.1.8.6"
|
||||
versionCode 10808
|
||||
versionName "0.1.8.8"
|
||||
// Fallback fuer Libraries mit Product Flavors
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aria-cockpit",
|
||||
"version": "0.1.8.6",
|
||||
"version": "0.1.8.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
@@ -497,6 +497,49 @@ const SettingsScreen: React.FC = () => {
|
||||
})();
|
||||
}
|
||||
|
||||
// Datei-Manager: Einzel-Datei-Download. ChatScreen subscribet auch auf
|
||||
// file_response — der versucht aber nur Chat-Bubble-Attachments zu
|
||||
// patchen und macht nix wenn die requestId nicht zu einer Nachricht
|
||||
// passt. Hier behandeln wir die Manager-initiierten Downloads
|
||||
// (requestId-Praefix 'single-' aus bulkDownload). Schreibt nach
|
||||
// ~/Download/ wie der ZIP-Pfad.
|
||||
if (message.type === ('file_response' as any)) {
|
||||
const p: any = message.payload || {};
|
||||
const reqId = (p.requestId as string) || '';
|
||||
if (!reqId.startsWith('single-')) return; // nicht unsere Anfrage
|
||||
if (p.error) {
|
||||
ToastAndroid.show('Download fehlgeschlagen: ' + p.error, ToastAndroid.LONG);
|
||||
return;
|
||||
}
|
||||
const b64 = (p.base64 as string) || '';
|
||||
if (!b64) return;
|
||||
const fileName = (p.name as string) ||
|
||||
(p.serverPath as string || '').split('/').pop() ||
|
||||
'aria-download';
|
||||
(async () => {
|
||||
try {
|
||||
const dir = RNFS.DownloadDirectoryPath;
|
||||
const filePath = `${dir}/${fileName}`;
|
||||
// Falls Datei schon existiert: Suffix anhaengen damit nichts
|
||||
// ueberschrieben wird.
|
||||
let target = filePath;
|
||||
let i = 1;
|
||||
while (await RNFS.exists(target)) {
|
||||
const dot = fileName.lastIndexOf('.');
|
||||
const base = dot > 0 ? fileName.slice(0, dot) : fileName;
|
||||
const ext = dot > 0 ? fileName.slice(dot) : '';
|
||||
target = `${dir}/${base} (${i})${ext}`;
|
||||
i++;
|
||||
}
|
||||
await RNFS.writeFile(target, b64, 'base64');
|
||||
const sizeKb = Math.round(((b64.length * 0.75)) / 1024);
|
||||
ToastAndroid.show(`Gespeichert: ${target.split('/').pop()} (${sizeKb} KB)`, ToastAndroid.LONG);
|
||||
} catch (e: any) {
|
||||
ToastAndroid.show('Speichern fehlgeschlagen: ' + e.message, ToastAndroid.LONG);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Voice wurde gespeichert → Liste neu laden + ggf. auswaehlen
|
||||
if (message.type === ('xtts_voice_saved' as any)) {
|
||||
const name = (message.payload as any).name as string;
|
||||
|
||||
@@ -344,23 +344,38 @@ class WakeWordService {
|
||||
/** Konversation beenden — User hat im Window nichts gesagt.
|
||||
* Mit Wake-Word: zurueck zu 'armed' (Listener wieder an).
|
||||
* Ohne: zurueck zu 'off'.
|
||||
*
|
||||
* WICHTIG: setzt bargeListening=false BEVOR OpenWakeWord.start() laeuft.
|
||||
* Grund: wenn endConversation aus dem onPlaybackFinished-Handler kommt,
|
||||
* feuert direkt danach ein zweiter Listener (stopBargeListening) — der
|
||||
* wuerde sonst OpenWakeWord.stop() rufen weil bargeListening noch true
|
||||
* ist, und unseren frisch re-armierten Listener killen.
|
||||
*/
|
||||
async endConversation(): Promise<void> {
|
||||
if (this.state !== 'conversing') {
|
||||
// Nicht in conversing — typ. nach App-Resume bevor Streaming endete.
|
||||
// Trotzdem loggen damit wir's im Diagnostic sehen.
|
||||
import('./logger').then(m => m.reportAppDebug('wake.end',
|
||||
`endConversation called but state=${this.state} → noop`)).catch(()=>{});
|
||||
return;
|
||||
}
|
||||
const wasBarge = this.bargeListening;
|
||||
// Flag NULLEN bevor wir die Listener triggern. Sonst killt der parallele
|
||||
// stopBargeListening-Listener (TTS-end) gleich danach unseren Native-
|
||||
// OpenWakeWord, weil er bargeListening=true sieht und annimmt er muss
|
||||
// den Listener stoppen.
|
||||
this.bargeListening = false;
|
||||
import('./logger').then(m => m.reportAppDebug('wake.end',
|
||||
`endConversation called, nativeReady=${this.nativeReady}, calling OpenWakeWord.start()`)).catch(()=>{});
|
||||
`endConversation called, wasBarge=${wasBarge}, nativeReady=${this.nativeReady}`)).catch(()=>{});
|
||||
if (this.nativeReady && OpenWakeWord) {
|
||||
// Wenn wakeword schon laeuft (war Barge-Listener waehrend TTS):
|
||||
// OpenWakeWord.start() ist idempotent (Kotlin checkt running.get()
|
||||
// und resolved sofort). Wir koennen es trotzdem rufen — billiger
|
||||
// als state extra zu fragen, garantiert dass nach diesem Pfad
|
||||
// Native auch wirklich an ist falls es out-of-band gestoppt wurde.
|
||||
try {
|
||||
await OpenWakeWord.start();
|
||||
console.log('[WakeWord] Konversation zu Ende — zurueck zu armed');
|
||||
console.log('[WakeWord] Konversation zu Ende — zurueck zu armed (wasBarge=%s)', wasBarge);
|
||||
import('./logger').then(m => m.reportAppDebug('wake.end',
|
||||
`OpenWakeWord.start() OK → state=armed, keyword=${this.keyword}`)).catch(()=>{});
|
||||
`OpenWakeWord.start() OK → state=armed, wasBarge=${wasBarge}`)).catch(()=>{});
|
||||
ToastAndroid.show(`Lausche wieder auf "${KEYWORD_LABELS[this.keyword]}"`, ToastAndroid.SHORT);
|
||||
this.setState('armed');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user