- Tabs: Meine Musik, Suchen, Download, Favoriten (vorher Download vor Suchen) - "Meine Musik": Schnellzugriffe (Favoriten/Wiedergabelisten/Zuletzt) einzeln über die Einstellungen abschaltbar - Audio- und Benachrichtigungs-Berechtigung werden jetzt beim ersten Start gemeinsam angefragt statt erst bei Bedarf; Hinweisbanner + Einstellungen- Button bei dauerhafter Verweigerung (auch für Benachrichtigungen neu) - Cover-Extraktion beim Android-Scan von 512px auf 1024px erhöht, für ein schärferes Cover im Sperrbildschirm - Fix: notifyListeners() in LibraryService nur noch bei echter Wertänderung (verhinderte einen Test-Hang durch den neuen Start-Berechtigungs-Aufruf) 557 Tests grün, flutter analyze ohne Befund. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VM2JK5mV7AL1g2Rt6H6h9w
119 lines
4.8 KiB
Dart
119 lines
4.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// Nutzer-Einstellungen, die die ganze App betreffen. Werden beim Start
|
|
/// einmal geladen und bei jeder Änderung sofort gespeichert.
|
|
class AppSettings extends ChangeNotifier {
|
|
static const _keyGroupCovers = 'gleiche_kategorie_gleiches_cover';
|
|
static const _keyAutoPlayHeadphones = 'autoplay_kopfhoerer';
|
|
static const _keyNormalizeVolume = 'gleiche_lautstaerke';
|
|
static const _keyCacheGrenze = 'cache_grenze_mb';
|
|
static const _keyMeineMusikSektionen = 'meine_musik_sektionen';
|
|
|
|
/// Schnellzugriffe oben in „Meine Musik" — Schlüssel für die
|
|
/// [meineMusikSektionen]-Auswahl in den Einstellungen.
|
|
static const sektionFavoriten = 'favoriten';
|
|
static const sektionWiedergabelisten = 'wiedergabelisten';
|
|
static const sektionZuletzt = 'zuletzt';
|
|
|
|
/// Alle wählbaren Sektionen, in der Reihenfolge, in der sie oben erscheinen.
|
|
static const alleMeineMusikSektionen = [
|
|
sektionFavoriten,
|
|
sektionWiedergabelisten,
|
|
sektionZuletzt,
|
|
];
|
|
|
|
/// Voreinstellung für den Zwischenspeicher: 2 GB. Groß genug für einige
|
|
/// hundert Titel, klein genug, um ein Telefon nicht zuzumüllen.
|
|
static const cacheGrenzeStandardMb = 2048;
|
|
|
|
/// Zur Auswahl stehende Grenzen in MB. 0 heißt: gar nicht zwischenspeichern.
|
|
static const cacheGrenzenMb = [0, 512, 1024, 2048, 4096, 8192];
|
|
|
|
bool _groupCoversByCategory = true;
|
|
bool _autoPlayOnHeadphones = false;
|
|
bool _normalizeVolume = true;
|
|
int _cacheGrenzeMb = cacheGrenzeStandardMb;
|
|
Set<String> _meineMusikSektionen = Set.of(alleMeineMusikSektionen);
|
|
|
|
/// "Gleiche Kategorie = gleiches Coverbild" — an: das Cover kommt von der
|
|
/// ersten Kategorie des Songs, aus: jeder Song zeigt sein eigenes Bild.
|
|
bool get groupCoversByCategory => _groupCoversByCategory;
|
|
|
|
/// Wiedergabe automatisch starten, sobald Kopfhörer verbunden werden.
|
|
bool get autoPlayOnHeadphones => _autoPlayOnHeadphones;
|
|
|
|
/// "Gleiche Lautstärke": laute Titel werden anhand ihres ReplayGain-Tags
|
|
/// auf das Niveau der leiseren abgesenkt.
|
|
bool get normalizeVolume => _normalizeVolume;
|
|
|
|
/// Obergrenze des Zwischenspeichers in MB. Gilt nur für das, was beim
|
|
/// Abspielen nebenbei anfällt — bewusst heruntergeladene Titel liegen in
|
|
/// einem eigenen Ordner und werden nie verdrängt.
|
|
int get cacheGrenzeMb => _cacheGrenzeMb;
|
|
|
|
int get cacheGrenzeBytes => _cacheGrenzeMb * 1024 * 1024;
|
|
|
|
/// Welche Schnellzugriffe oben in „Meine Musik" angezeigt werden.
|
|
Set<String> get meineMusikSektionen => _meineMusikSektionen;
|
|
|
|
Future<void> init() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
_groupCoversByCategory = prefs.getBool(_keyGroupCovers) ?? true;
|
|
_autoPlayOnHeadphones = prefs.getBool(_keyAutoPlayHeadphones) ?? false;
|
|
_normalizeVolume = prefs.getBool(_keyNormalizeVolume) ?? true;
|
|
_cacheGrenzeMb = prefs.getInt(_keyCacheGrenze) ?? cacheGrenzeStandardMb;
|
|
final gespeicherteSektionen = prefs.getStringList(_keyMeineMusikSektionen);
|
|
_meineMusikSektionen = gespeicherteSektionen == null
|
|
? Set.of(alleMeineMusikSektionen)
|
|
: Set.of(gespeicherteSektionen);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> setCacheGrenzeMb(int mb) async {
|
|
if (_cacheGrenzeMb == mb) return;
|
|
_cacheGrenzeMb = mb;
|
|
notifyListeners();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setInt(_keyCacheGrenze, mb);
|
|
}
|
|
|
|
Future<void> setGroupCoversByCategory(bool value) async {
|
|
if (_groupCoversByCategory == value) return;
|
|
_groupCoversByCategory = value;
|
|
notifyListeners();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_keyGroupCovers, value);
|
|
}
|
|
|
|
Future<void> setNormalizeVolume(bool value) async {
|
|
if (_normalizeVolume == value) return;
|
|
_normalizeVolume = value;
|
|
notifyListeners();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_keyNormalizeVolume, value);
|
|
}
|
|
|
|
Future<void> setAutoPlayOnHeadphones(bool value) async {
|
|
if (_autoPlayOnHeadphones == value) return;
|
|
_autoPlayOnHeadphones = value;
|
|
notifyListeners();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_keyAutoPlayHeadphones, value);
|
|
}
|
|
|
|
/// Blendet eine Schnellzugriff-Sektion oben in „Meine Musik" ein oder aus.
|
|
Future<void> setMeineMusikSektionAktiv(String sektion, bool aktiv) async {
|
|
final aktuell = aktiv == _meineMusikSektionen.contains(sektion);
|
|
if (aktuell) return;
|
|
_meineMusikSektionen = Set.of(_meineMusikSektionen);
|
|
aktiv
|
|
? _meineMusikSektionen.add(sektion)
|
|
: _meineMusikSektionen.remove(sektion);
|
|
notifyListeners();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setStringList(
|
|
_keyMeineMusikSektionen, _meineMusikSektionen.toList());
|
|
}
|
|
}
|