Fix: Review-Findings korrigiert (Tag-Leiste, Profil, Singleton u.a.)
CRIT: - Tag-Leiste: Toggle-Kopf 'Tags & Filter' eingebaut (war unerreichbar) + TagStats - Profil: Cloud-Count vor Dialog aufloesen (kein 'Instance of Future' mehr) HIGH: - CloudEinstellungen: toten Sync-Timer entfernt (echter Timer im ViewModel) - StatistikCard + RecentWidget jetzt eingebaut (gesamtMB/gesamtMin endlich genutzt) - CloudService als Singleton (konsistenter Token-Zustand in allen Services) MED/LOW: - Playlist-Erkennung nur noch via list= Parameter - Player: fehlende Quelle wird geloggt statt still - song_tile: null-ID-Guard vor Tag-Dialog - Scanner-Log mit Exception-Objekt - FavoritenService: anzahlFavoriten() fuer StatistikCard
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import 'dart:async';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import '../models/song.dart';
|
||||
import 'player_service.dart';
|
||||
|
||||
/// Hintergrund-Audio-Handler für Sperrbildschirm & Benachrichtigung
|
||||
class MeloAudioHandler extends BaseAudioHandler {
|
||||
final PlayerService _player = PlayerService();
|
||||
|
||||
MeloAudioHandler() {
|
||||
// Zustand vom Player an audio_service weiterleiten
|
||||
_player.stateStream.listen(_updateState);
|
||||
_player.positionStream.listen((pos) {
|
||||
if (_playing) {
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
updatePosition: pos,
|
||||
));
|
||||
}
|
||||
});
|
||||
_player.onSongWechsel.listen((song) {
|
||||
if (song != null) {
|
||||
mediaItem.add(_toMediaItem(song));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _playing = false;
|
||||
|
||||
void _updateState(PlayerState state) {
|
||||
_playing = state.playing;
|
||||
playbackState.add(PlaybackState(
|
||||
controls: _playing
|
||||
? [MediaControl.pause, MediaControl.skipToPrevious, MediaControl.skipToNext, MediaControl.stop]
|
||||
: [MediaControl.play, MediaControl.skipToPrevious, MediaControl.skipToNext, MediaControl.stop],
|
||||
systemActions: const {MediaAction.seek},
|
||||
androidCompactActionIndices: const [0, 1, 2],
|
||||
processingState: _playing ? AudioProcessingState.ready : AudioProcessingState.idle,
|
||||
playing: _playing,
|
||||
speed: 1.0,
|
||||
));
|
||||
}
|
||||
|
||||
MediaItem _toMediaItem(Song song) => MediaItem(
|
||||
id: song.id?.toString() ?? '0',
|
||||
album: song.album.isEmpty ? 'Melo' : song.album,
|
||||
title: song.titel,
|
||||
artist: song.kuenstler,
|
||||
duration: Duration(seconds: song.dauerSekunden),
|
||||
artUri: song.coverPfad != null ? Uri.file(song.coverPfad!) : null,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> play() async {
|
||||
if (_player.aktuellerSong != null) {
|
||||
_playing = true;
|
||||
await _player.playPause();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pause() async {
|
||||
_playing = false;
|
||||
await _player.playPause();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
_playing = false;
|
||||
await _player.playPause();
|
||||
playbackState.add(playbackState.value.copyWith(
|
||||
controls: [MediaControl.play],
|
||||
playing: false,
|
||||
processingState: AudioProcessingState.idle,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> seek(Duration position) async {
|
||||
_player.spiele(_player.aktuellerSong!, position: position.inSeconds);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> skipToNext() => _player.naechstes();
|
||||
|
||||
@override
|
||||
Future<void> skipToPrevious() => _player.vorheriges();
|
||||
|
||||
// Exposed for the ViewModel
|
||||
PlayerService get player => _player;
|
||||
}
|
||||
Reference in New Issue
Block a user