mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-28 12:22:12 +02:00
fix: restore default quality settings
This commit is contained in:
@@ -25,6 +25,26 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||||||
final hasDownloadExtensions = extensionState.extensions.any(
|
final hasDownloadExtensions = extensionState.extensions.any(
|
||||||
(extension) => extension.enabled && extension.hasDownloadProvider,
|
(extension) => extension.enabled && extension.hasDownloadProvider,
|
||||||
);
|
);
|
||||||
|
final selectedDownloadService = resolveEffectiveDownloadService(
|
||||||
|
settings.defaultService,
|
||||||
|
extensionState,
|
||||||
|
);
|
||||||
|
final selectedDownloadExtension = extensionState.extensions
|
||||||
|
.where(
|
||||||
|
(extension) =>
|
||||||
|
extension.enabled &&
|
||||||
|
extension.hasDownloadProvider &&
|
||||||
|
extension.id == selectedDownloadService,
|
||||||
|
)
|
||||||
|
.firstOrNull;
|
||||||
|
final qualityOptions =
|
||||||
|
selectedDownloadExtension?.qualityOptions ?? const <QualityOption>[];
|
||||||
|
final canSelectQuality = qualityOptions.isNotEmpty;
|
||||||
|
final isTidalService = selectedDownloadService.isNotEmpty
|
||||||
|
? ref
|
||||||
|
.read(extensionProvider.notifier)
|
||||||
|
.downloadProviderMatchesBuiltIn(selectedDownloadService, 'tidal')
|
||||||
|
: false;
|
||||||
final nativeWorkerAvailable = Platform.isAndroid && hasDownloadExtensions;
|
final nativeWorkerAvailable = Platform.isAndroid && hasDownloadExtensions;
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
final topPadding = normalizedHeaderTopPadding(context);
|
final topPadding = normalizedHeaderTopPadding(context);
|
||||||
@@ -101,16 +121,51 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||||||
SettingsSwitchItem(
|
SettingsSwitchItem(
|
||||||
icon: Icons.tune,
|
icon: Icons.tune,
|
||||||
title: context.l10n.downloadAskBeforeDownload,
|
title: context.l10n.downloadAskBeforeDownload,
|
||||||
subtitle: hasDownloadExtensions
|
subtitle: !hasDownloadExtensions
|
||||||
|
? context.l10n.extensionsNoDownloadProvider
|
||||||
|
: canSelectQuality
|
||||||
? context.l10n.downloadAskQualitySubtitle
|
? context.l10n.downloadAskQualitySubtitle
|
||||||
: context.l10n.extensionsNoDownloadProvider,
|
: context.l10n.downloadSelectServiceToEnable,
|
||||||
value: settings.askQualityBeforeDownload,
|
value: settings.askQualityBeforeDownload,
|
||||||
enabled: hasDownloadExtensions,
|
enabled: hasDownloadExtensions && canSelectQuality,
|
||||||
onChanged: (value) => ref
|
onChanged: (value) => ref
|
||||||
.read(settingsProvider.notifier)
|
.read(settingsProvider.notifier)
|
||||||
.setAskQualityBeforeDownload(value),
|
.setAskQualityBeforeDownload(value),
|
||||||
showDivider: false,
|
|
||||||
),
|
),
|
||||||
|
if (!settings.askQualityBeforeDownload &&
|
||||||
|
canSelectQuality) ...[
|
||||||
|
for (final quality in qualityOptions)
|
||||||
|
_QualityOption(
|
||||||
|
title: _localizedQualityLabel(context, quality),
|
||||||
|
subtitle: _localizedQualityDescription(
|
||||||
|
context,
|
||||||
|
quality,
|
||||||
|
),
|
||||||
|
icon: _qualityIcon(quality.id),
|
||||||
|
isSelected: settings.audioQuality == quality.id,
|
||||||
|
onTap: () => ref
|
||||||
|
.read(settingsProvider.notifier)
|
||||||
|
.setAudioQuality(quality.id),
|
||||||
|
showDivider:
|
||||||
|
quality != qualityOptions.last ||
|
||||||
|
(isTidalService && settings.audioQuality == 'HIGH'),
|
||||||
|
),
|
||||||
|
if (isTidalService && settings.audioQuality == 'HIGH')
|
||||||
|
SettingsItem(
|
||||||
|
icon: Icons.tune,
|
||||||
|
title: context.l10n.downloadLossyFormat,
|
||||||
|
subtitle: _getTidalHighFormatLabel(
|
||||||
|
context,
|
||||||
|
settings.tidalHighFormat,
|
||||||
|
),
|
||||||
|
onTap: () => _showTidalHighFormatPicker(
|
||||||
|
context,
|
||||||
|
ref,
|
||||||
|
settings.tidalHighFormat,
|
||||||
|
),
|
||||||
|
showDivider: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -266,6 +321,161 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
|||||||
return name == null ? effective : '$effective - $name';
|
return name == null ? effective : '$effective - $name';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IconData _qualityIcon(String qualityId) {
|
||||||
|
final normalized = qualityId.toUpperCase();
|
||||||
|
if (normalized.startsWith('MP3_') || normalized == 'MP3') {
|
||||||
|
return Icons.audiotrack;
|
||||||
|
}
|
||||||
|
if (normalized.startsWith('OPUS_') || normalized == 'OPUS') {
|
||||||
|
return Icons.graphic_eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (normalized) {
|
||||||
|
case 'HI_RES_LOSSLESS':
|
||||||
|
return Icons.four_k;
|
||||||
|
case 'HI_RES':
|
||||||
|
return Icons.high_quality;
|
||||||
|
case 'LOSSLESS':
|
||||||
|
return Icons.music_note;
|
||||||
|
default:
|
||||||
|
return Icons.music_note;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _localizedQualityLabel(BuildContext context, QualityOption quality) {
|
||||||
|
switch (quality.id.toUpperCase()) {
|
||||||
|
case 'LOSSLESS':
|
||||||
|
return context.l10n.qualityFlacLossless;
|
||||||
|
case 'HI_RES':
|
||||||
|
return context.l10n.qualityHiResFlac;
|
||||||
|
case 'HI_RES_LOSSLESS':
|
||||||
|
return context.l10n.qualityHiResFlacMax;
|
||||||
|
case 'HIGH':
|
||||||
|
return context.l10n.downloadLossy320;
|
||||||
|
default:
|
||||||
|
return quality.label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _localizedQualityDescription(
|
||||||
|
BuildContext context,
|
||||||
|
QualityOption quality,
|
||||||
|
) {
|
||||||
|
switch (quality.id.toUpperCase()) {
|
||||||
|
case 'LOSSLESS':
|
||||||
|
return context.l10n.qualityFlacLosslessSubtitle;
|
||||||
|
case 'HI_RES':
|
||||||
|
return context.l10n.qualityHiResFlacSubtitle;
|
||||||
|
case 'HI_RES_LOSSLESS':
|
||||||
|
return context.l10n.qualityHiResFlacMaxSubtitle;
|
||||||
|
case 'HIGH':
|
||||||
|
return _getTidalHighFormatLabel(
|
||||||
|
context,
|
||||||
|
ref.read(settingsProvider).tidalHighFormat,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return quality.description ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getTidalHighFormatLabel(BuildContext context, String format) {
|
||||||
|
switch (format) {
|
||||||
|
case 'mp3_320':
|
||||||
|
return context.l10n.downloadLossyMp3;
|
||||||
|
case 'opus_256':
|
||||||
|
return context.l10n.downloadLossyOpus256;
|
||||||
|
case 'opus_128':
|
||||||
|
return context.l10n.downloadLossyOpus128;
|
||||||
|
default:
|
||||||
|
return context.l10n.downloadLossyMp3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showTidalHighFormatPicker(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
String current,
|
||||||
|
) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
backgroundColor: colorScheme.surfaceContainerHigh,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(28)),
|
||||||
|
),
|
||||||
|
builder: (context) => SafeArea(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||||
|
child: Text(
|
||||||
|
context.l10n.downloadLossy320Format,
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||||
|
child: Text(
|
||||||
|
context.l10n.downloadLossy320FormatDesc,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.audiotrack),
|
||||||
|
title: Text(context.l10n.downloadLossyMp3),
|
||||||
|
subtitle: Text(context.l10n.downloadLossyMp3Subtitle),
|
||||||
|
trailing: current == 'mp3_320'
|
||||||
|
? Icon(Icons.check, color: colorScheme.primary)
|
||||||
|
: null,
|
||||||
|
onTap: () {
|
||||||
|
ref
|
||||||
|
.read(settingsProvider.notifier)
|
||||||
|
.setTidalHighFormat('mp3_320');
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.graphic_eq),
|
||||||
|
title: Text(context.l10n.downloadLossyOpus256),
|
||||||
|
subtitle: Text(context.l10n.downloadLossyOpus256Subtitle),
|
||||||
|
trailing: current == 'opus_256'
|
||||||
|
? Icon(Icons.check, color: colorScheme.primary)
|
||||||
|
: null,
|
||||||
|
onTap: () {
|
||||||
|
ref
|
||||||
|
.read(settingsProvider.notifier)
|
||||||
|
.setTidalHighFormat('opus_256');
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.graphic_eq),
|
||||||
|
title: Text(context.l10n.downloadLossyOpus128),
|
||||||
|
subtitle: Text(context.l10n.downloadLossyOpus128Subtitle),
|
||||||
|
trailing: current == 'opus_128'
|
||||||
|
? Icon(Icons.check, color: colorScheme.primary)
|
||||||
|
: null,
|
||||||
|
onTap: () {
|
||||||
|
ref
|
||||||
|
.read(settingsProvider.notifier)
|
||||||
|
.setTidalHighFormat('opus_128');
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showNetworkModePicker(
|
void _showNetworkModePicker(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
@@ -629,6 +839,39 @@ class _BetaBadge extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _QualityOption extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final IconData icon;
|
||||||
|
final bool isSelected;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final bool showDivider;
|
||||||
|
|
||||||
|
const _QualityOption({
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.icon,
|
||||||
|
required this.isSelected,
|
||||||
|
required this.onTap,
|
||||||
|
this.showDivider = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
return SettingsItem(
|
||||||
|
icon: icon,
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
trailing: isSelected
|
||||||
|
? Icon(Icons.check, color: colorScheme.primary)
|
||||||
|
: null,
|
||||||
|
onTap: onTap,
|
||||||
|
showDivider: showDivider,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ServiceSelector extends ConsumerWidget {
|
class _ServiceSelector extends ConsumerWidget {
|
||||||
final String currentService;
|
final String currentService;
|
||||||
final ValueChanged<String> onChanged;
|
final ValueChanged<String> onChanged;
|
||||||
|
|||||||
Reference in New Issue
Block a user