diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7208d24..814a6b1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -36,6 +36,13 @@ + + + () + ?.createNotificationChannel(fgChannel); + await FlutterBackgroundService().configure( iosConfiguration: IosConfiguration(), androidConfiguration: AndroidConfiguration( onStart: _onServiceStart, autoStart: false, isForegroundMode: true, + notificationChannelId: 'de.baka.melo.fg_service', + initialNotificationTitle: 'Melo Sync', + initialNotificationContent: 'Synchronisiere Musik…', + foregroundServiceTypes: [AndroidForegroundType.dataSync], ), ); diff --git a/lib/services/sync_service.dart b/lib/services/sync_service.dart index 1e5bcb9..cb955c8 100644 --- a/lib/services/sync_service.dart +++ b/lib/services/sync_service.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import '../database/db_helper.dart'; import '../models/song.dart'; @@ -577,18 +578,22 @@ class SyncService { if (!Platform.isAndroid) return; final maxP = gesamt > 0 ? gesamt : 1; final p = aktuell > maxP ? maxP : aktuell; - notificationsPlugin.show( - id: _syncNotifyId, - title: 'Synchronisiere…', - body: gesamt > 0 ? '$aktuell / $gesamt Songs' : body, - notificationDetails: NotificationDetails( - android: syncNotificationDetails( - ongoing: true, - progress: p, - maxProgress: maxP, + try { + notificationsPlugin.show( + id: _syncNotifyId, + title: 'Synchronisiere…', + body: gesamt > 0 ? '$aktuell / $gesamt Songs' : body, + notificationDetails: NotificationDetails( + android: syncNotificationDetails( + ongoing: true, + progress: p, + maxProgress: maxP, + ), ), - ), - ); + ); + } catch (_) { + // Plugin kann beim App-Exit bereits disposed sein + } } /// Abschluss-Notification — tippbar (öffnet den Cloud-Tab, payload @@ -630,9 +635,18 @@ class SyncService { /// Startet den Foreground-Service, damit der Sync auch bei Display-aus /// und minimierter App weiterläuft (Android-Prozess-Garantie). + /// v2.55.3: Prüft POST_NOTIFICATIONS-Permission (Android 13+). + /// Ohne diese Permission crasht der Foreground-Service innerhalb von + /// 5 Sekunden (Android killt Services ohne sichtbare Notification). Future _starteForegroundService() async { if (!Platform.isAndroid) return; try { + // Android 13+: Benachrichtigungs-Berechtigung erforderlich + final status = await Permission.notification.status; + if (status.isDenied || status.isPermanentlyDenied) { + MeloLogger().aktion('foreground_sync_skip', {'grund': 'notification_denied'}); + return; + } final svc = FlutterBackgroundService(); final laeuft = await svc.isRunning(); if (!laeuft) {