/** * Auto-Update Service — prueft und installiert App-Updates via RVS * * Flow: * 1. App sendet "update_check" mit aktueller Version an RVS * 2. RVS vergleicht → sendet "update_available" mit Download-URL * 3. App zeigt Benachrichtigung → User bestaetigt → Download + Install */ import { Alert, Linking, Platform } from 'react-native'; import RNFS from 'react-native-fs'; import rvs, { RVSMessage } from './rvs'; // Aktuelle App-Version (aus package.json via Build) const APP_VERSION = '0.0.2.3'; // TODO: aus nativer Build-Config lesen type UpdateCallback = (info: UpdateInfo) => void; export interface UpdateInfo { version: string; downloadUrl: string; size: number; } class UpdateService { private listeners: UpdateCallback[] = []; private checking = false; private downloading = false; constructor() { // Auf update_available Nachrichten lauschen rvs.onMessage((msg: RVSMessage) => { if (msg.type === 'update_available' as any) { const info: UpdateInfo = { version: (msg.payload.version as string) || '', downloadUrl: (msg.payload.downloadUrl as string) || '', size: (msg.payload.size as number) || 0, }; if (info.version && this.isNewer(info.version)) { console.log(`[Update] Neue Version verfuegbar: ${info.version} (aktuell: ${APP_VERSION})`); this.listeners.forEach(cb => cb(info)); } } }); } /** Bei App-Start Update pruefen */ checkForUpdate(): void { if (this.checking) return; this.checking = true; console.log(`[Update] Pruefe auf Updates (aktuell: ${APP_VERSION})`); rvs.send('update_check' as any, { version: APP_VERSION }); setTimeout(() => { this.checking = false; }, 10000); } /** Callback registrieren */ onUpdateAvailable(callback: UpdateCallback): () => void { this.listeners.push(callback); return () => { this.listeners = this.listeners.filter(cb => cb !== callback); }; } /** Update-Dialog anzeigen */ promptUpdate(info: UpdateInfo): void { const sizeMB = (info.size / 1024 / 1024).toFixed(1); Alert.alert( 'ARIA Update verfuegbar', `Version ${info.version} (${sizeMB} MB)\n\nAktuell: ${APP_VERSION}\n\nJetzt herunterladen und installieren?`, [ { text: 'Spaeter', style: 'cancel' }, { text: 'Installieren', onPress: () => this.downloadAndInstall(info), }, ], ); } /** APK ueber WebSocket herunterladen und installieren */ async downloadAndInstall(info: UpdateInfo): Promise { if (this.downloading) return; this.downloading = true; try { console.log(`[Update] Fordere APK v${info.version} an...`); Alert.alert('Download gestartet', `Version ${info.version} wird ueber RVS heruntergeladen...`); // APK ueber WebSocket anfordern rvs.send('update_download' as any, {}); // Auf update_data warten (einmalig) const apkData = await new Promise<{base64: string, fileName: string}>((resolve, reject) => { const timeout = setTimeout(() => reject(new Error('Download-Timeout (60s)')), 60000); const unsub = rvs.onMessage((msg: RVSMessage) => { if ((msg.type as string) === 'update_data') { clearTimeout(timeout); unsub(); if (msg.payload.error) { reject(new Error(msg.payload.error as string)); } else { resolve({ base64: msg.payload.base64 as string, fileName: msg.payload.fileName as string || `ARIA-${info.version}.apk`, }); } } }); }); // Base64 als APK-Datei speichern const destPath = `${RNFS.CachesDirectoryPath}/${apkData.fileName}`; await RNFS.writeFile(destPath, apkData.base64, 'base64'); const fileSize = await RNFS.stat(destPath); console.log(`[Update] APK gespeichert: ${destPath} (${(parseInt(fileSize.size) / 1024 / 1024).toFixed(1)}MB)`); // APK installieren (oeffnet Android-Installer) if (Platform.OS === 'android') { await Linking.openURL(`file://${destPath}`); } } catch (err: any) { console.error(`[Update] Fehler: ${err.message}`); Alert.alert('Update fehlgeschlagen', err.message); } finally { this.downloading = false; } } /** Versionsvergleich */ private isNewer(remote: string): boolean { const r = remote.split('.').map(Number); const l = APP_VERSION.split('.').map(Number); for (let i = 0; i < Math.max(r.length, l.length); i++) { const diff = (r[i] || 0) - (l[i] || 0); if (diff > 0) return true; if (diff < 0) return false; } return false; } getCurrentVersion(): string { return APP_VERSION; } } const updateService = new UpdateService(); export default updateService;