v2.41 — Issue #3: Musik-Download im Hintergrund (flutter_background_service)

- flutter_background_service ^5.0.0 hinzugefügt
- AndroidManifest: FOREGROUND_SERVICE_DATA_SYNC Permission
- main.dart: Background Service mit configure() initialisiert
- _onServiceStart(): Service-Callback für Foreground-Garantie
- DownloadService._startBackgroundService(): Foreground Service vor Download
- DownloadService._stopBackgroundService(): Stopp nach Batch/Single
- Integration in _resetStatus, downloadBatch, downloadVonUrl
This commit is contained in:
Dustin
2026-08-02 16:06:04 +02:00
parent b8c206e58f
commit ae11a85872
5 changed files with 94 additions and 1 deletions
+1
View File
@@ -2,6 +2,7 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
+24
View File
@@ -5,6 +5,7 @@ import 'package:audio_service/audio_service.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'database/db_helper.dart'; import 'database/db_helper.dart';
import 'services/favoriten_service.dart'; import 'services/favoriten_service.dart';
import 'services/auth_service.dart'; import 'services/auth_service.dart';
@@ -47,6 +48,16 @@ void main() async {
AndroidFlutterLocalNotificationsPlugin>() AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(downloadChannel); ?.createNotificationChannel(downloadChannel);
// ── Background Service initialisieren (Issue #3) ──
await FlutterBackgroundService().configure(
iosConfiguration: IosConfiguration(),
androidConfiguration: AndroidConfiguration(
onStart: _onServiceStart,
autoStart: false,
isForegroundMode: true,
),
);
try { try {
await DbHelper().db; await DbHelper().db;
await FavoritenService().init(); await FavoritenService().init();
@@ -148,6 +159,19 @@ Future<void> _starteAutoSyncFallsNoetig() async {
} }
} }
/// Background Service Callback hält App im Vordergrund beim Download (Issue #3)
@pragma('vm:entry-point')
Future<bool> _onServiceStart(ServiceInstance service) async {
WidgetsFlutterBinding.ensureInitialized();
// Service läuft als Foreground-Service und hält den Prozess am Leben.
// Der eigentliche Download läuft im Haupt-Isolate der Service dient nur
// als WakeLock / Foreground-Garantie.
service.on('stopService').listen((_) {
service.stopSelf();
});
return true;
}
class MeloApp extends StatelessWidget { class MeloApp extends StatelessWidget {
const MeloApp({super.key}); const MeloApp({super.key});
+36 -1
View File
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import '../models/song.dart'; import '../models/song.dart';
import '../database/db_helper.dart'; import '../database/db_helper.dart';
import '../utils/audio_validator.dart'; import '../utils/audio_validator.dart';
@@ -66,13 +67,46 @@ class DownloadService extends ChangeNotifier {
_aktuellerTitel = null; _aktuellerTitel = null;
_fehler = null; _fehler = null;
_abbruchCompleter = Completer<void>(); _abbruchCompleter = Completer<void>();
_startBackgroundService();
notifyListeners(); notifyListeners();
} }
/// Issue #3: Foreground Service starten hält App im Hintergrund aktiv
void _startBackgroundService() {
try {
final svc = FlutterBackgroundService();
svc.isRunning().then((running) {
if (!running) {
svc.startService();
}
});
debugPrint('🔵 Background Service gestartet');
} catch (e) {
debugPrint('Background Service Start fehlgeschlagen: $e');
}
}
/// Issue #3: Foreground Service stoppen
void _stopBackgroundService() {
try {
final svc = FlutterBackgroundService();
svc.isRunning().then((running) {
if (running) {
svc.invoke('stopService');
}
});
debugPrint('🔴 Background Service gestoppt');
} catch (e) {
debugPrint('Background Service Stop fehlgeschlagen: $e');
}
}
/// Song von YouTube herunterladen /// Song von YouTube herunterladen
Future<Song?> downloadVonUrl(String url) async { Future<Song?> downloadVonUrl(String url) async {
_resetStatus(); _resetStatus();
return _downloadEinzeln(url); final result = await _downloadEinzeln(url);
if (!_ladt) _stopBackgroundService();
return result;
} }
/// Mehrere URLs/Playlists nacheinander mit Cooldown /// Mehrere URLs/Playlists nacheinander mit Cooldown
@@ -147,6 +181,7 @@ class DownloadService extends ChangeNotifier {
} }
notifyListeners(); notifyListeners();
_completeNotification(erfolgreich, urls.length, abgebrochen: sollAbbrechen); _completeNotification(erfolgreich, urls.length, abgebrochen: sollAbbrechen);
_stopBackgroundService();
MeloLogger().zustand('download_batch_ende', { MeloLogger().zustand('download_batch_ende', {
'erfolgreich': erfolgreich, 'erfolgreich': erfolgreich,
'gesamt': urls.length, 'gesamt': urls.length,
+32
View File
@@ -150,6 +150,38 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_background_service:
dependency: "direct main"
description:
name: flutter_background_service
sha256: "70a1c185b1fa1a44f8f14ecd6c86f6e50366e3562f00b2fa5a54df39b3324d3d"
url: "https://pub.dev"
source: hosted
version: "5.1.0"
flutter_background_service_android:
dependency: transitive
description:
name: flutter_background_service_android
sha256: ca0793d4cd19f1e194a130918401a3d0b1076c81236f7273458ae96987944a87
url: "https://pub.dev"
source: hosted
version: "6.3.1"
flutter_background_service_ios:
dependency: transitive
description:
name: flutter_background_service_ios
sha256: "6037ffd45c4d019dab0975c7feb1d31012dd697e25edc05505a4a9b0c7dc9fba"
url: "https://pub.dev"
source: hosted
version: "5.0.3"
flutter_background_service_platform_interface:
dependency: transitive
description:
name: flutter_background_service_platform_interface
sha256: ca74aa95789a8304f4d3f57f07ba404faa86bed6e415f83e8edea6ad8b904a41
url: "https://pub.dev"
source: hosted
version: "5.1.2"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
description: description:
+1
View File
@@ -30,6 +30,7 @@ dependencies:
crypto: ^3.0.6 crypto: ^3.0.6
flutter_secure_storage: ^10.3.1 flutter_secure_storage: ^10.3.1
flutter_local_notifications: ^22.2.0 flutter_local_notifications: ^22.2.0
flutter_background_service: ^5.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: