Fuenf P0-Fehler behoben: Benachrichtigung, Offline, Cover, Warteschlange
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
This commit is contained in:
co-authored by
Claude Opus 5
parent
34d6d8e52a
commit
e6d5f1c9ee
@@ -0,0 +1,361 @@
|
||||
# Design- & Feature-Review: Melo vs. Apple Music / Spotify
|
||||
|
||||
**Review-ID:** melo_vollwertigkeit_20260823
|
||||
**Datum:** 2026-08-23
|
||||
**Umfang:** gesamte App (`lib/`, 56 Dateien, 14.721 Zeilen), Android-Manifest, Referenz-Bilder
|
||||
**Fokus:** Visual · Usability · Funktionsumfang · Code
|
||||
**Werkzeuge:** ui-ux-pro-max (design-system + flutter stack + pro-rules), ui-design:design-review
|
||||
|
||||
**Verifizierter Ist-Zustand:** `flutter analyze` → *No issues found*. `flutter test` → *328 bestanden, 1 übersprungen* (der bekannte Hänger aus BACKLOG.md).
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Melo ist technisch überdurchschnittlich sauber: echtes Design-Token-System, WCAG-geprüfte
|
||||
Textfarben, Tombstone-fähiges Schema ab Tag 1, hohe Testabdeckung, deutschsprachige
|
||||
Erklär-Kommentare an genau den Stellen, wo früher Bugs saßen. Das ist deutlich besser als
|
||||
der Durchschnitt eines Hobby-Projekts.
|
||||
|
||||
Als **vollwertige Musik-App im Sinne von Apple Music ist sie es aber noch nicht.** Es fehlen
|
||||
nicht Details, sondern drei tragende Säulen: **Alben als Browse-Achse**, **echtes Offline-
|
||||
Management** und **Cover-Art für Server-Titel**. Dazu kommen zwei stille Fehler, die man
|
||||
der App nicht ansieht: der Offline-Schalter tut nichts, und ohne `POST_NOTIFICATIONS`
|
||||
erscheint auf Android 13+ keine Wiedergabe-Benachrichtigung.
|
||||
|
||||
**Gefundene Punkte:** 26 → 5 kritisch · 10 groß · 7 klein · 4 Vorschläge
|
||||
|
||||
> **Stand 2026-08-23, nach der Umsetzung:** Die fünf P0-Punkte (K1–K5) sind
|
||||
> behoben, siehe CHANGELOG. G9 und G10 kamen bei der Umsetzung dazu und
|
||||
> sind offen.
|
||||
|
||||
---
|
||||
|
||||
## Kritische Punkte (P0)
|
||||
|
||||
### K1 — Offline-Modus ist ein toter Schalter
|
||||
**Ort:** `lib/services/offline_mode.dart` · `lib/settings/settings_screen.dart:190`
|
||||
**Kategorie:** Usability / Korrektheit
|
||||
|
||||
`OfflineMode` speichert ein Bool in den SharedPreferences und ruft `notifyListeners()`.
|
||||
Das war's. Verifiziert per Volltextsuche: **kein einziger Leser** außerhalb von
|
||||
`main.dart` (Registrierung) und dem Settings-Schalter selbst. `MeloAudioHandler`,
|
||||
`NavidromeService` und `SyncService` fragen ihn nie ab.
|
||||
|
||||
**Wirkung:** Der Nutzer schaltet "Nur gecachte Lieder abspielen" ein, die App streamt
|
||||
trotzdem und verbraucht Mobilfunkdaten. Ein Versprechen, das die UI gibt und der Code
|
||||
bricht.
|
||||
|
||||
**Empfehlung:** `OfflineMode` in `loadPlaylist` auslesen. Ist er an, nur Titel mit
|
||||
Cache-Treffer oder lokalem Pfad in die Queue nehmen und die übersprungenen sichtbar
|
||||
machen ("3 Titel übersprungen — nicht offline verfügbar").
|
||||
|
||||
---
|
||||
|
||||
### K2 — Keine `POST_NOTIFICATIONS`-Berechtigung
|
||||
**Ort:** `android/app/src/main/AndroidManifest.xml`
|
||||
**Kategorie:** Plattform
|
||||
|
||||
Verifiziert im **gemergten** Manifest
|
||||
(`build/app/intermediates/merged_manifests/release/.../AndroidManifest.xml`):
|
||||
`POST_NOTIFICATIONS` ist nicht dabei. `targetSdk` folgt Flutter (≥ 33).
|
||||
|
||||
**Wirkung:** Auf Android 13+ zeigt das System die Media-Notification erst nach
|
||||
erteilter Laufzeit-Berechtigung. Ohne sie fehlt die zentrale Bedienfläche einer
|
||||
Musik-App im Alltag. Der Vordergrunddienst läuft, aber unsichtbar.
|
||||
|
||||
**Empfehlung:** Permission ins Manifest, beim ersten Start der Wiedergabe abfragen
|
||||
(`permission_handler` ist bereits als Abhängigkeit da).
|
||||
|
||||
---
|
||||
|
||||
### K3 — Server-Titel haben nie ein Coverbild
|
||||
**Ort:** `lib/shared/cover.dart:38` · `lib/downloads/downloads_screen.dart:309`
|
||||
**Kategorie:** Visual
|
||||
|
||||
`CoverImage` rendert **ausschließlich** `artUri.scheme == 'file'`. Für alles andere
|
||||
kommt der Noten-Platzhalter. Navidrome-Titel bekommen in `_playAlbum`/`_playArtist`
|
||||
gar kein `artUri` mitgegeben — obwohl `NavidromeService.coverUrl` (Zeile 265) existiert
|
||||
und `SubsonicAlbum.coverId` geparst wird.
|
||||
|
||||
**Wirkung:** Jeder Server-Titel ist in Liste, Mini-Player, Vollbild-Player, Notification
|
||||
und Sperrbildschirm ein graues Notensymbol. Das ist der augenfälligste Unterschied zu
|
||||
Apple Music — dort trägt das Cover die gesamte visuelle Identität.
|
||||
|
||||
**Empfehlung:** `CoverImage` um `Image.network` für http/https erweitern (mit
|
||||
`cacheWidth`, Fehler-Fallback auf den Platzhalter), und in beiden `_play*`-Methoden
|
||||
`artUri: _nav.coverUrl(s.coverId)` setzen. Bild-Cache lokal ablegen, damit es offline
|
||||
bleibt.
|
||||
|
||||
---
|
||||
|
||||
### K4 — Alben fehlen als Browse-Achse
|
||||
**Ort:** `lib/library/song_grouping.dart` · `lib/library/my_music_screen.dart:88`
|
||||
**Kategorie:** Funktionsumfang
|
||||
|
||||
`Songs.album` ist eine Textspalte, aber es gibt weder eine Alben-Liste noch einen
|
||||
Album-Detail-Screen. `song_grouping.dart` kann nur `groupByArtist`. Die Unterreiter
|
||||
heißen **Lieder / Kategorie / Künstler** — im Referenz-Screenshot
|
||||
(`ui-referenz/bugs/img_2d5761551808.jpg`, das ist Melo selbst) stand dort noch
|
||||
**Songs / Künstler / Alben**. Die Achse ist also verlorengegangen.
|
||||
|
||||
**Wirkung:** Album ist neben Playlist die wichtigste Einheit, in der Menschen Musik
|
||||
denken. Ohne sie ist "Bibliothek" eine flache Titelliste — brauchbar bei 200 Titeln,
|
||||
unbrauchbar bei 5.000.
|
||||
|
||||
**Empfehlung:** `groupByAlbum` (analog zu `groupByArtist`, existierende `_groupBy`-
|
||||
Hilfsfunktion nutzt man mit), vierter Unterreiter, Album-Screen mit großem Cover,
|
||||
Künstler, Jahr, Titelzahl, Gesamtdauer und "Abspielen / Zufällig".
|
||||
|
||||
---
|
||||
|
||||
### K5 — Warteschlange neu ordnen startet die Wiedergabe neu
|
||||
**Ort:** `lib/player/queue_screen.dart:87,103` · `lib/player/audio_handler.dart:169`
|
||||
**Kategorie:** Usability / Korrektheit
|
||||
|
||||
Der Kommentar im Code ist ehrlich: der Handler bietet keine Live-Mutation, also ruft
|
||||
jedes Entfernen und jedes Verschieben `loadPlaylist()` auf. Das führt
|
||||
`_player.setAudioSources(...)` + `play()` + `seek()` aus.
|
||||
|
||||
Drei Folgen:
|
||||
1. **Hörbarer Aussetzer** bei jeder Queue-Änderung — bei Apple Music passiert das lautlos.
|
||||
2. `loadPlaylist` setzt `_lastCountedIndex = null` (Zeile 173) → der laufende Titel wird
|
||||
**erneut als Wiedergabe gezählt**. Die Sortierung "Wie oft abgespielt" wird durch
|
||||
Queue-Bedienung verfälscht.
|
||||
3. Bei Server-Titeln werden alle Cache-Treffer neu ermittelt (`await` in der Schleife).
|
||||
|
||||
**Empfehlung:** `removeQueueItem`/`insertQueueItem` auf `_player.removeAudioSourceAt` /
|
||||
`moveAudioSource` abbilden. `just_audio` kann das ohne Neuaufbau.
|
||||
|
||||
---
|
||||
|
||||
## Große Punkte (P1)
|
||||
|
||||
### G1 — Suche findet nur lokale Titel, nur flach
|
||||
**Ort:** `lib/library/search_screen.dart` (59 Zeilen)
|
||||
`db.searchSongs()` per LIKE über Titel/Künstler/Album. Kein Server (`search3.view` wird
|
||||
nirgends aufgerufen), keine Playlisten, keine Kategorien, keine Gruppierung der Treffer
|
||||
nach Typ, keine letzten Suchen, keine Vorschläge, kein Entprellen. Apple Music gruppiert
|
||||
Ergebnisse in Titel/Alben/Künstler/Playlists und zeigt bei leerem Feld die Historie.
|
||||
Der ganze Tab ist heute ein Textfeld über einer Liste.
|
||||
|
||||
### G2 — Navidrome ist im Download-Tab versteckt
|
||||
**Ort:** `lib/downloads/downloads_screen.dart:56`
|
||||
Der komplette Server-Browser ist ein Unterreiter von "Download". Ein Nutzer, der seine
|
||||
Server-Bibliothek hören will, sucht sie nicht unter "Download". Im Referenz-Screenshot
|
||||
hieß der Tab noch "Online" — treffender.
|
||||
|
||||
Dazu: Tippen auf ein Album **spielt sofort ab**. Es gibt keinen Album-Screen, keine
|
||||
Titelliste, keine Möglichkeit, einen einzelnen Server-Titel zu wählen. Nur 50 Alben,
|
||||
kein Nachladen, keine Server-Suche.
|
||||
|
||||
### G3 — Server-Titel sind Bürger zweiter Klasse
|
||||
**Ort:** `lib/library/song_media.dart` vs. `lib/downloads/downloads_screen.dart:309`
|
||||
Ein `MediaItem` vom Server trägt nur `navidromeId` — **kein** `songId`. Damit gilt für
|
||||
Server-Titel: kein Favorit, keine Playlist-Zuordnung, kein lokaler Wiedergabezähler,
|
||||
keine Kategorie, kein Cover, kein Eintrag in der Historie.
|
||||
|
||||
**Konkreter Fehler daraus:** `now_playing_screen.dart:45` gibt
|
||||
`FavoriteButton(songId: item.extras?['songId'] ?? '')` — bei Server-Titeln also den
|
||||
**leeren String**. `FavoriteButton` prüft nicht darauf, `toggleFavorite('')` legt eine
|
||||
Favoriten-Zeile mit leerer ID an. Das Herz füllt sich, in der Favoritenliste (INNER JOIN
|
||||
auf `songs`) taucht nichts auf. Falsche Rückmeldung an den Nutzer.
|
||||
|
||||
### G4 — Kein gezielter Offline-Download
|
||||
**Ort:** `lib/services/cache_manager.dart` · `lib/player/audio_handler.dart:203`
|
||||
Gecacht wird nur, was zufällig abgespielt wurde. Es gibt kein "Album offline verfügbar
|
||||
machen", keine Anzeige, welche Titel offline sind, kein Verwalten pro Titel.
|
||||
`CacheManager` hat außerdem **keine Größenbegrenzung und keine Verdrängung** — nur
|
||||
"alles löschen". Der Cache wächst unbegrenzt. Alle Dateien werden `.mp3` benannt,
|
||||
unabhängig vom tatsächlichen Codec (Navidrome liefert oft Opus/FLAC).
|
||||
|
||||
### G5 — Kein "läuft gerade"-Zustand in der Liste
|
||||
**Ort:** `lib/library/song_list.dart:157`
|
||||
Die Referenz-UI zeigt den laufenden Titel farbig hervorgehoben mit animierten Balken.
|
||||
Melos `SongList` hat keinerlei Bezug zum `mediaItem`-Stream. Der Nutzer scrollt durch die
|
||||
Liste und sieht nicht, wo er gerade ist.
|
||||
|
||||
### G6 — Zwei verschiedene Rot-Töne
|
||||
**Ort:** 10 Vorkommen von `Colors.redAccent` (#FF5252), meist in `settings_screen.dart`
|
||||
und `downloads_screen.dart`
|
||||
Die Marke ist `MeloTheme.red` = #C0392B (gedecktes Ziegelrot). `Colors.redAccent` ist ein
|
||||
grelles Signalrot. Beide stehen im selben Screen nebeneinander. Insgesamt **58
|
||||
hartcodierte `Colors.*`-Werte** außerhalb des Themes, 18 davon allein in den
|
||||
Einstellungen — die sind visuell der schwächste Screen der App.
|
||||
|
||||
### G7 — Emoji als Icon
|
||||
**Ort:** `downloads_screen.dart:234,247` (📀 Alben / 🎤 Künstler),
|
||||
`settings_screen.dart:190` (📴 Offline-Modus), `now_playing_screen.dart:362` (📝 Songtext)
|
||||
Verstößt gegen die erste Regel der Pre-Delivery-Checkliste von ui-ux-pro-max: Emoji
|
||||
rendern je nach Hersteller-Font unterschiedlich, lassen sich nicht einfärben, folgen
|
||||
keinem Design-Token und sind für Screenreader Unsinn. Material-Icons sind vorhanden
|
||||
(`Icons.album`, `Icons.mic`, `Icons.cloud_off`, `Icons.lyrics`).
|
||||
|
||||
### G8 — Songtext ist ein statischer Textblock
|
||||
**Ort:** `lib/player/now_playing_screen.dart:309`
|
||||
Der Text wird als eine Wand angezeigt. Kein Zeilen-Sync (LRC), kein Mitlaufen, kein
|
||||
Antippen zum Springen. Navidromes `getLyricsBySongId` liefert synchronisierte Texte.
|
||||
Das ist bei Apple Music eine der meistgenutzten Funktionen.
|
||||
|
||||
---
|
||||
|
||||
### G9 — Nach `stop()` merkt sich die App keine Positionen mehr *(bei der P0-Umsetzung gefunden)*
|
||||
**Ort:** `lib/player/audio_handler.dart:258` (`stop`)
|
||||
**Kategorie:** Korrektheit · **Status:** nicht behoben, außerhalb des P0-Auftrags
|
||||
|
||||
`stop()` ruft `_positionRecordTimer.cancel()`. Der Timer ist `Timer.periodic`
|
||||
und wird nur **einmal im Konstruktor** angelegt — einmal abgebrochen, kommt er
|
||||
für den Rest der App-Sitzung nicht zurück.
|
||||
|
||||
`stop()` löst nicht nur der Nutzer aus: das System ruft es beim Wegwischen der
|
||||
Benachrichtigung und über die Stop-Aktion auf. Danach speichert die App keine
|
||||
Wiedergabepositionen mehr und scrobbelt nicht mehr zum Server — bis zum
|
||||
Neustart, und ohne jeden Hinweis. „Fortsetzen an alter Stelle" hört damit
|
||||
still auf zu funktionieren.
|
||||
|
||||
**Empfehlung:** Den Timer in `stop()` nicht abbrechen, sondern in der Callback
|
||||
prüfen (`if (!_player.playing) return;` steht bereits drin — der Abbruch ist
|
||||
also gar nicht nötig). Abbrechen gehört allein in `dispose()`.
|
||||
|
||||
**Vorbestehend**, nicht durch die P0-Änderungen entstanden: der alte
|
||||
`QueueScreen` rief in derselben Lage ebenfalls `handler.stop()` auf.
|
||||
|
||||
---
|
||||
|
||||
### G10 — Server-Cover haben keinen dauerhaften Zwischenspeicher *(Folge der K3-Umsetzung)*
|
||||
**Ort:** `lib/shared/cover.dart:55`
|
||||
**Kategorie:** Performance · **Status:** offen, bewusst nicht in P0
|
||||
|
||||
`Image.network` legt nur im Arbeitsspeicher ab (Flutters `ImageCache`, ~100 MB,
|
||||
beim Beenden weg). Beim nächsten Kaltstart fragt jede sichtbare Zeile ihr Cover
|
||||
erneut beim Server an. Außerdem laufen diese Anfragen auch im Offline-Modus —
|
||||
sie schlagen dann fehl und fallen auf den Platzhalter zurück, kosten aber
|
||||
Versuche.
|
||||
|
||||
Ein dauerhafter Cache bräuchte entweder `cached_network_image` als neue
|
||||
Abhängigkeit oder eine Erweiterung des vorhandenen `CacheManager` auf Bilder.
|
||||
Beides ist eine Entscheidung, keine Fehlerbehebung — deshalb nicht im P0-Fix.
|
||||
Der Unterschied zu vorher bleibt trotzdem groß: bisher gab es **überhaupt kein**
|
||||
Server-Cover.
|
||||
|
||||
---
|
||||
|
||||
## Kleine Punkte (P2)
|
||||
|
||||
| # | Punkt | Ort |
|
||||
|---|---|---|
|
||||
| P1 | AppBar der Einstellungen heißt **"Settings"** — einziges englisches Wort in einer deutschen App | `settings_screen.dart:65` |
|
||||
| P2 | Kein `Hero`-Übergang Mini-Player → Vollbild (0 Treffer in `lib/`). Der Wechsel springt, statt das Cover wandern zu lassen | `mini_player.dart:70` |
|
||||
| P3 | Kein Wischen am Mini-Player (nach unten = schließen, seitlich = nächster Titel) | `mini_player.dart` |
|
||||
| P4 | Künstlerliste ist eine nackte `ListTile`-Liste ohne Bilder; kein Album-Zwischenschritt | `artist_list.dart:31` |
|
||||
| P5 | Keine Reduced-Motion-Unterstützung (0 Treffer für `disableAnimations`), obwohl `MeloMotion` sauber getokent ist | `theme.dart:41` |
|
||||
| P6 | Keine Querformat-/Tablet-Anpassung (`LayoutBuilder` nur in `cover.dart`, `MediaQuery.size` nur im Equalizer) | app-weit |
|
||||
| P7 | Kein Alphabet-Schnellscroller und keine Mehrfachauswahl in langen Listen | `song_list.dart` |
|
||||
|
||||
---
|
||||
|
||||
## Vorschläge (P3)
|
||||
|
||||
- **V1 — Farbe aus dem Cover ziehen.** Apple Music und Spotify färben den Player nach dem
|
||||
Albumcover. In Flutter mit `PaletteGenerator` + Verlauf hinter dem Cover. Größter
|
||||
optischer Sprung pro Zeile Code.
|
||||
- **V2 — Eigene Schrift.** Die App nutzt System-Roboto. Die ui-ux-pro-max-Abfrage für
|
||||
"music entertainment" schlägt Righteous/Poppins vor; für Schwarz/Rot passt eher eine
|
||||
kräftige Grotesk (Outfit, Sora) für Überschriften bei Roboto/Inter im Fließtext.
|
||||
- **V3 — YouTube-Share-Intent.** Aktuell muss man die URL kopieren und einfügen. Ein
|
||||
`ACTION_SEND`-Intent-Filter im Manifest macht "Teilen → Melo" möglich. Sehr kleine
|
||||
Änderung, spürbarer Alltagsgewinn.
|
||||
- **V4 — Android Auto / Assistant.** Der `MediaBrowserService` ist im Manifest deklariert,
|
||||
aber `MeloAudioHandler` überschreibt `getChildren`/`getMediaItem` nicht — Browsen im Auto
|
||||
funktioniert also nicht. Kein Widget, keine Wear-Unterstützung.
|
||||
|
||||
---
|
||||
|
||||
## Was gut ist (nicht ändern)
|
||||
|
||||
- **Design-Token-System** (`theme.dart`): `MeloSpace`, `MeloRadius`, `MeloMotion` mit
|
||||
begründeten Werten. Die Textfarben sind nicht geschätzt, sondern per
|
||||
`theme_kontrast_test.dart` gegen 4,5:1 nachgerechnet. Das machen wenige.
|
||||
- **Haarlinie statt Schatten** auf Schwarz — richtige Entscheidung für OLED, deckt sich mit
|
||||
der "Dark Mode (OLED)"-Empfehlung des Style-Katalogs.
|
||||
- **Datenmodell**: UUID + `updatedAtMs` + `deleted`-Tombstone ab Schema 1, 9 saubere
|
||||
Migrationen. Sync war von Anfang an mitgedacht statt nachgerüstet.
|
||||
- **Reine Funktionen ausgelagert** (`shouldResumeAt`, `playNextIndex`, `reorderQueue`,
|
||||
`planeSync`, `newPlayingIndex`) — deshalb 328 Tests ohne Emulator.
|
||||
- **Kommentare erklären das Warum**, oft mit dem konkreten Bug, der dahinterstand
|
||||
(`navidromeIdOf`, `cacheSchluessel`, `loadPlaylist`). Genau die Stellen aus
|
||||
`ui-referenz/bugs/` sind so dokumentiert.
|
||||
- **Tooltips überall** an Icon-Buttons → in Flutter automatisch Semantics-Label.
|
||||
|
||||
---
|
||||
|
||||
## Priorisierter Plan
|
||||
|
||||
### P0 — bevor irgendetwas Neues gebaut wird
|
||||
1. `POST_NOTIFICATIONS` ins Manifest + Laufzeit-Abfrage *(K2, ~1 h)*
|
||||
2. Offline-Modus wirklich verdrahten oder den Schalter entfernen *(K1, ~3 h)*
|
||||
3. `CoverImage` für http + `artUri` bei Server-Titeln setzen *(K3, ~3 h)*
|
||||
4. Queue ohne Neuladen mutieren, Doppelzählung beenden *(K5, ~4 h)*
|
||||
5. Leere `songId` in `FavoriteButton` abfangen *(G3-Teil, ~30 min)*
|
||||
|
||||
### P1 — das macht sie "vollwertig"
|
||||
6. Alben-Achse: `groupByAlbum`, vierter Unterreiter, Album-Screen *(K4, ~1 Tag)*
|
||||
7. Suche umbauen: nach Typ gruppiert, Server einbeziehen, letzte Suchen *(G1, ~1,5 Tage)*
|
||||
8. Server-Bereich aus "Download" lösen → eigener Tab "Online" mit Album-Detail *(G2, ~1,5 Tage)*
|
||||
9. Offline-Download pro Album/Playlist + Cache-Grenze mit LRU *(G4, ~1,5 Tage)*
|
||||
10. "Läuft gerade"-Markierung in allen Listen *(G5, ~3 h)*
|
||||
|
||||
### P2 — Politur, die man sofort sieht
|
||||
11. `Colors.redAccent` → `MeloTheme.red`, Emoji → Material-Icons, "Settings" → "Einstellungen" *(G6, G7, P1 — zusammen ~3 h)*
|
||||
12. Hero-Übergang + Wischgesten am Mini-Player *(P2, P3, ~4 h)*
|
||||
13. Synchronisierter Songtext (LRC) *(G8, ~1 Tag)*
|
||||
14. Künstler- und Kategorieliste visuell aufwerten *(P4, ~4 h)*
|
||||
15. Reduced-Motion + Querformat *(P5, P6, ~4 h)*
|
||||
|
||||
### P3 — danach
|
||||
16. Cover-Farbe im Player *(V1)*
|
||||
17. Eigene Schrift *(V2)*
|
||||
18. YouTube-Share-Intent *(V3)*
|
||||
19. Android Auto: `getChildren` implementieren *(V4)*
|
||||
|
||||
---
|
||||
|
||||
## Feature-Matrix gegen Apple Music
|
||||
|
||||
| Bereich | Apple Music | Melo | Bewertung |
|
||||
|---|---|---|---|
|
||||
| Wiedergabe, Hintergrund, Sperrbildschirm | ✅ | ✅ | **da** (Notification-Permission fehlt → K2) |
|
||||
| Warteschlange ansehen/umordnen | ✅ | ✅ | **schwach** — Neuladen bei jeder Änderung |
|
||||
| Als Nächstes / Ans Ende | ✅ | ✅ | **da** |
|
||||
| Shuffle & Repeat | ✅ | ✅ | **da** |
|
||||
| Sleep-Timer | ✅ | ✅ | **da** (Melo besser als Apple Music) |
|
||||
| Equalizer | ⚠️ nur System | ✅ 5-Band + Bass + Virtualizer | **besser als Apple Music** |
|
||||
| Lautstärke-Angleich | ✅ | ✅ ReplayGain | **da** |
|
||||
| Fortsetzen an alter Stelle | ✅ | ✅ lokal + Server-Bookmark | **da** |
|
||||
| Favoriten | ✅ | ✅ | **da**, aber nicht für Server-Titel |
|
||||
| Playlisten anlegen/ordnen | ✅ | ✅ | **da** |
|
||||
| Alben durchsuchen | ✅ | ❌ | **fehlt** |
|
||||
| Künstler-Seite mit Alben | ✅ | ⚠️ nackte Titelliste | **schwach** |
|
||||
| Suche | ✅ typgruppiert, serverweit | ⚠️ lokal, flach | **schwach** |
|
||||
| Songtext | ✅ zeilensynchron | ⚠️ statischer Block | **schwach** |
|
||||
| Offline verfügbar machen | ✅ gezielt | ⚠️ nur Zufalls-Cache | **schwach** |
|
||||
| Cover-Art | ✅ | ⚠️ nur lokale Dateien | **schwach** |
|
||||
| Geräte-Sync der Bibliothek | ✅ iCloud | ✅ Melo-Cloud + Tombstones | **da** |
|
||||
| Scrobble / Wiedergabestatistik | ✅ | ✅ lokal + Subsonic | **da** |
|
||||
| Musikerkennung (Shazam) | ✅ | ✅ ACRCloud | **da** |
|
||||
| YouTube-Import | ❌ | ✅ | **Alleinstellung** |
|
||||
| Kategorien / eigene Tags | ❌ | ✅ | **Alleinstellung** |
|
||||
| Empfehlungen, Radio, Charts | ✅ | ❌ | **fehlt** (bewusst? Melo hat keinen Katalog) |
|
||||
| Teilen | ✅ | ❌ | **fehlt** |
|
||||
| Homescreen-Widget | ✅ | ❌ | **fehlt** |
|
||||
| Android Auto | ✅ | ❌ | **fehlt** (Service da, Browsing nicht) |
|
||||
| Cast / Sonos | ✅ | ❌ | **fehlt** |
|
||||
| Crossfade / Gapless | ✅ | ❌ | **fehlt** |
|
||||
|
||||
**Zählung:** 13 × da · 7 × schwach · 8 × fehlt · 3 × besser als Apple Music
|
||||
|
||||
---
|
||||
|
||||
_Erzeugt mit ui-ux-pro-max 2.13.0 (design-system, --stack flutter, pro-rules) und
|
||||
ui-design:design-review. Alle Code-Stellen im Repo verifiziert, `flutter analyze` und
|
||||
`flutter test` zum Review-Zeitpunkt ausgeführt._
|
||||
Reference in New Issue
Block a user