v2.55.3 — Code-Review-Fixes: foregroundServiceType (Android 14+), POST_NOTIFICATIONS-Check, FG-Service-Notification-Channel, try/catch _zeigeSyncNotification
CRITICAL: - AndroidManifest: foregroundServiceType="dataSync" für BackgroundService (Android 14+ MissingForegroundServiceTypeException) - main.dart: notificationChannelId + initialNotificationTitle + foregroundServiceTypes für FlutterBackgroundService (fehlende FG-Notification → ANR/crash nach 5s) MEDIUM: - sync_service.dart: POST_NOTIFICATIONS-Permission-Check vor _starteForegroundService (Android 13+) - sync_service.dart: try/catch um _zeigeSyncNotification (plugin-dispose-Sicherheit) flutter analyze: clean | flutter test: 194/194 passed
This commit is contained in:
@@ -36,6 +36,13 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<!-- v2.55.3: Foreground-Service-Type für Android 14+ (compileSdk 36).
|
||||||
|
Ohne dieses Attribut crasht der Service-Start mit
|
||||||
|
MissingForegroundServiceTypeException. -->
|
||||||
|
<service
|
||||||
|
android:name="id.flutter.flutter_background_service.BackgroundService"
|
||||||
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -77,12 +77,33 @@ void main() async {
|
|||||||
?.createNotificationChannel(syncChannel);
|
?.createNotificationChannel(syncChannel);
|
||||||
|
|
||||||
// ── Background Service initialisieren (Issue #3) ──
|
// ── Background Service initialisieren (Issue #3) ──
|
||||||
|
// v2.55.3: Foreground-Service-Notification-Channel konfigurieren.
|
||||||
|
// Ohne notificationChannelId zeigt der Service eine Default-Notification
|
||||||
|
// ohne eigenen Channel — auf Android 14+ Pflicht.
|
||||||
|
const fgChannel = AndroidNotificationChannel(
|
||||||
|
'de.baka.melo.fg_service',
|
||||||
|
'Melo Hintergrunddienst',
|
||||||
|
description: 'Hält Melo im Hintergrund aktiv (Sync, Downloads)',
|
||||||
|
importance: Importance.low,
|
||||||
|
playSound: false,
|
||||||
|
enableVibration: false,
|
||||||
|
showBadge: false,
|
||||||
|
);
|
||||||
|
await notificationsPlugin
|
||||||
|
.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin>()
|
||||||
|
?.createNotificationChannel(fgChannel);
|
||||||
|
|
||||||
await FlutterBackgroundService().configure(
|
await FlutterBackgroundService().configure(
|
||||||
iosConfiguration: IosConfiguration(),
|
iosConfiguration: IosConfiguration(),
|
||||||
androidConfiguration: AndroidConfiguration(
|
androidConfiguration: AndroidConfiguration(
|
||||||
onStart: _onServiceStart,
|
onStart: _onServiceStart,
|
||||||
autoStart: false,
|
autoStart: false,
|
||||||
isForegroundMode: true,
|
isForegroundMode: true,
|
||||||
|
notificationChannelId: 'de.baka.melo.fg_service',
|
||||||
|
initialNotificationTitle: 'Melo Sync',
|
||||||
|
initialNotificationContent: 'Synchronisiere Musik…',
|
||||||
|
foregroundServiceTypes: [AndroidForegroundType.dataSync],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.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 'package:flutter_background_service/flutter_background_service.dart';
|
||||||
import '../database/db_helper.dart';
|
import '../database/db_helper.dart';
|
||||||
import '../models/song.dart';
|
import '../models/song.dart';
|
||||||
@@ -577,18 +578,22 @@ class SyncService {
|
|||||||
if (!Platform.isAndroid) return;
|
if (!Platform.isAndroid) return;
|
||||||
final maxP = gesamt > 0 ? gesamt : 1;
|
final maxP = gesamt > 0 ? gesamt : 1;
|
||||||
final p = aktuell > maxP ? maxP : aktuell;
|
final p = aktuell > maxP ? maxP : aktuell;
|
||||||
notificationsPlugin.show(
|
try {
|
||||||
id: _syncNotifyId,
|
notificationsPlugin.show(
|
||||||
title: 'Synchronisiere…',
|
id: _syncNotifyId,
|
||||||
body: gesamt > 0 ? '$aktuell / $gesamt Songs' : body,
|
title: 'Synchronisiere…',
|
||||||
notificationDetails: NotificationDetails(
|
body: gesamt > 0 ? '$aktuell / $gesamt Songs' : body,
|
||||||
android: syncNotificationDetails(
|
notificationDetails: NotificationDetails(
|
||||||
ongoing: true,
|
android: syncNotificationDetails(
|
||||||
progress: p,
|
ongoing: true,
|
||||||
maxProgress: maxP,
|
progress: p,
|
||||||
|
maxProgress: maxP,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
} catch (_) {
|
||||||
|
// Plugin kann beim App-Exit bereits disposed sein
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abschluss-Notification — tippbar (öffnet den Cloud-Tab, payload
|
/// 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
|
/// Startet den Foreground-Service, damit der Sync auch bei Display-aus
|
||||||
/// und minimierter App weiterläuft (Android-Prozess-Garantie).
|
/// 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<void> _starteForegroundService() async {
|
Future<void> _starteForegroundService() async {
|
||||||
if (!Platform.isAndroid) return;
|
if (!Platform.isAndroid) return;
|
||||||
try {
|
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 svc = FlutterBackgroundService();
|
||||||
final laeuft = await svc.isRunning();
|
final laeuft = await svc.isRunning();
|
||||||
if (!laeuft) {
|
if (!laeuft) {
|
||||||
|
|||||||
Reference in New Issue
Block a user