diff --git a/android/android/app/src/main/AndroidManifest.xml b/android/android/app/src/main/AndroidManifest.xml index 3c533d4..b287d8a 100644 --- a/android/android/app/src/main/AndroidManifest.xml +++ b/android/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + + + + + diff --git a/android/android/app/src/main/java/com/ariacockpit/ApkInstallerModule.kt b/android/android/app/src/main/java/com/ariacockpit/ApkInstallerModule.kt new file mode 100644 index 0000000..6c676a7 --- /dev/null +++ b/android/android/app/src/main/java/com/ariacockpit/ApkInstallerModule.kt @@ -0,0 +1,44 @@ +package com.ariacockpit + +import android.content.Intent +import android.net.Uri +import android.os.Build +import androidx.core.content.FileProvider +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.Promise +import java.io.File + +class ApkInstallerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + override fun getName() = "ApkInstaller" + + @ReactMethod + fun install(filePath: String, promise: Promise) { + try { + val file = File(filePath) + if (!file.exists()) { + promise.reject("FILE_NOT_FOUND", "APK nicht gefunden: $filePath") + return + } + + val context = reactApplicationContext + val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) + } else { + Uri.fromFile(file) + } + + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, "application/vnd.android.package-archive") + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + context.startActivity(intent) + promise.resolve(true) + } catch (e: Exception) { + promise.reject("INSTALL_ERROR", e.message, e) + } + } +} diff --git a/android/android/app/src/main/java/com/ariacockpit/ApkInstallerPackage.kt b/android/android/app/src/main/java/com/ariacockpit/ApkInstallerPackage.kt new file mode 100644 index 0000000..2282536 --- /dev/null +++ b/android/android/app/src/main/java/com/ariacockpit/ApkInstallerPackage.kt @@ -0,0 +1,16 @@ +package com.ariacockpit + +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager + +class ApkInstallerPackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List { + return listOf(ApkInstallerModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/android/android/app/src/main/java/com/ariacockpit/MainApplication.kt b/android/android/app/src/main/java/com/ariacockpit/MainApplication.kt index adf68f7..16ab703 100644 --- a/android/android/app/src/main/java/com/ariacockpit/MainApplication.kt +++ b/android/android/app/src/main/java/com/ariacockpit/MainApplication.kt @@ -18,8 +18,7 @@ class MainApplication : Application(), ReactApplication { object : DefaultReactNativeHost(this) { override fun getPackages(): List = PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) + add(ApkInstallerPackage()) } override fun getJSMainModuleName(): String = "index" diff --git a/android/android/app/src/main/res/xml/file_paths.xml b/android/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..d2e2c8d --- /dev/null +++ b/android/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/src/screens/SettingsScreen.tsx b/android/src/screens/SettingsScreen.tsx index a49fe74..02367b4 100644 --- a/android/src/screens/SettingsScreen.tsx +++ b/android/src/screens/SettingsScreen.tsx @@ -748,11 +748,21 @@ const SettingsScreen: React.FC = () => { {'\u00DC'}ber ARIA Cockpit - Version 0.0.2.8 + Version {require('../../package.json').version} Stefans Kommandozentrale f{'\u00FC'}r ARIA.{'\n'} Gebaut mit React Native + TypeScript. + { + const updateService = require('../services/updater').default; + updateService.checkForUpdate(); + Alert.alert('Update-Check', 'Pruefe auf neue Version...'); + }} + > + Auf Updates pr{'\u00FC'}fen + {/* Platz am Ende */} diff --git a/android/src/services/updater.ts b/android/src/services/updater.ts index 90e79a3..b60c4a9 100644 --- a/android/src/services/updater.ts +++ b/android/src/services/updater.ts @@ -7,12 +7,13 @@ * 3. App zeigt Benachrichtigung → User bestaetigt → Download + Install */ -import { Alert, Linking, Platform } from 'react-native'; +import { Alert, Linking, Platform, NativeModules } 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 +// Version aus package.json (wird beim Build eingebettet) +const packageJson = require('../../package.json'); +const APP_VERSION = packageJson.version || '0.0.0.0'; type UpdateCallback = (info: UpdateInfo) => void; @@ -116,9 +117,17 @@ class UpdateService { 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) + // APK installieren via natives ApkInstaller Module (FileProvider + Intent) if (Platform.OS === 'android') { - await Linking.openURL(`file://${destPath}`); + try { + const { ApkInstaller } = NativeModules; + await ApkInstaller.install(destPath); + } catch (installErr: any) { + Alert.alert( + 'APK heruntergeladen', + `Version ${info.version} gespeichert.\n\nBitte manuell installieren:\nDateimanager → ${apkData.fileName} antippen.\n\n(${installErr.message})`, + ); + } } } catch (err: any) { console.error(`[Update] Fehler: ${err.message}`);