Ergebnis einer Durchsicht der App gegen den Massstab "Apple Music". Alle fuenf Punkte waren keine fehlenden Funktionen, sondern Versprechen, die die Oberflaeche gab und der Code nicht hielt. - POST_NOTIFICATIONS fehlte im Manifest. Auf Android 13+ blieb die Wiedergabe-Benachrichtigung dadurch unsichtbar. Das Recht wird erst erfragt, wenn wirklich ein Titel laeuft. - Der Offline-Schalter wurde nirgends gelesen. Er greift jetzt an allen drei Wegen in die Warteschlange; uebersprungene Titel werden benannt. - CoverImage kannte nur file://, und der Server-Browser gab die Cover-Adresse gar nicht erst mit. Server-Titel waren deshalb immer ein graues Notensymbol. - Warteschlange entfernen/umsortieren baute die ganze Queue neu auf: hoerbarer Aussetzer und doppelte Wiedergabe-Zaehlung. Jetzt mutiert nur der betroffene Eintrag, die Buchfuehrung steht vor dem Player. - Das Herz bei Server-Titeln legte einen Favoriten mit leerer Kennung an, der nie in der Liste auftauchte. Es ist jetzt ausgegraut. Nachgebessert aus dem Code-Review: Offline-Meldung an die Stelle geholt, wo sie greifen kann; Queue-Buchfuehrung vor den Player gezogen; Fehlerpfad fuer die Berechtigungs-Abfrage ergaenzt. 360 Tests gruen (vorher 328), flutter analyze ohne Befund, Release-APK gebaut und POST_NOTIFICATIONS darin nachgeprueft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
74 lines
2.8 KiB
Dart
74 lines
2.8 KiB
Dart
import 'package:audio_service/audio_service.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../shared/cover.dart';
|
|
import '../shared/theme.dart';
|
|
import 'audio_handler.dart';
|
|
|
|
/// Warteschlangen-Ansicht: aktuelle Queue anzeigen, zu einem Titel springen,
|
|
/// Titel entfernen oder per Drag & Drop umsortieren.
|
|
///
|
|
/// Der `MeloAudioHandler` mutiert die Warteschlange direkt
|
|
/// ([MeloAudioHandler.removeQueueItemAt], [MeloAudioHandler.moveQueueItem]) —
|
|
/// die laufende Wiedergabe bleibt dabei unangetastet.
|
|
class QueueScreen extends StatelessWidget {
|
|
const QueueScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final handler = context.read<MeloAudioHandler>();
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('Warteschlange')),
|
|
body: StreamBuilder<List<MediaItem>>(
|
|
stream: handler.queue,
|
|
builder: (context, queueSnap) {
|
|
final queue = queueSnap.data ?? const <MediaItem>[];
|
|
if (queue.isEmpty) {
|
|
return const Center(
|
|
child: Text('Warteschlange ist leer',
|
|
style: TextStyle(color: MeloTheme.text2)),
|
|
);
|
|
}
|
|
return StreamBuilder<MediaItem?>(
|
|
stream: handler.mediaItem,
|
|
builder: (context, currentSnap) {
|
|
final currentId = currentSnap.data?.id;
|
|
return ReorderableListView.builder(
|
|
itemCount: queue.length,
|
|
onReorderItem: (oldIndex, newIndex) =>
|
|
handler.moveQueueItem(oldIndex, newIndex),
|
|
itemBuilder: (context, i) {
|
|
final item = queue[i];
|
|
final playing = item.id == currentId;
|
|
return ListTile(
|
|
key: ValueKey(item.id),
|
|
leading: CoverImage(artUri: item.artUri, size: 48, radius: 6),
|
|
title: Text(
|
|
item.title,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: playing
|
|
? const TextStyle(
|
|
color: MeloTheme.red, fontWeight: FontWeight.bold)
|
|
: null,
|
|
),
|
|
subtitle: Text(item.artist ?? 'Unbekannt',
|
|
maxLines: 1, overflow: TextOverflow.ellipsis),
|
|
trailing: IconButton(
|
|
tooltip: 'Aus Warteschlange entfernen',
|
|
icon: const Icon(Icons.close),
|
|
onPressed: () => handler.removeQueueItemAt(i),
|
|
),
|
|
onTap: () => handler.skipToQueueItem(i),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|