diff --git a/lib/screens/album_screen.dart b/lib/screens/album_screen.dart index a6516270..5cd0d030 100644 --- a/lib/screens/album_screen.dart +++ b/lib/screens/album_screen.dart @@ -826,7 +826,7 @@ class _AlbumTrackItem extends ConsumerWidget { style: TextStyle(color: colorScheme.onSurfaceVariant), ), ), - if (isInLocalLibrary) ...[ + if (isInLocalLibrary || isInHistory) ...[ const SizedBox(width: 6), Container( padding: const EdgeInsets.symmetric( diff --git a/lib/screens/library_tracks_folder_screen.dart b/lib/screens/library_tracks_folder_screen.dart index 4198be85..3d66b196 100644 --- a/lib/screens/library_tracks_folder_screen.dart +++ b/lib/screens/library_tracks_folder_screen.dart @@ -798,7 +798,7 @@ class _LibraryTracksFolderScreenState _buildShuffleButton(entries), const SizedBox(width: 12), ], - _buildDownloadAllCenterButton(context, entries), + _buildPlayAllCenterButton(entries), ], ), ], @@ -831,7 +831,7 @@ class _LibraryTracksFolderScreenState ); } - // ── Shuffle / Download buttons ── + // ── Shuffle / Play buttons ── Widget _buildShuffleButton(List entries) { return Container( @@ -854,15 +854,12 @@ class _LibraryTracksFolderScreenState ); } - Widget _buildDownloadAllCenterButton( - BuildContext context, - List entries, - ) { + Widget _buildPlayAllCenterButton(List entries) { final tracks = entries.map((e) => e.track).toList(growable: false); return FilledButton.icon( - onPressed: tracks.isEmpty ? null : () => _downloadAll(context, tracks), - icon: const Icon(Icons.download_rounded, size: 18), - label: Text(context.l10n.downloadAllCount(tracks.length)), + onPressed: tracks.isEmpty ? null : () => _playAll(tracks), + icon: const Icon(Icons.play_arrow_rounded, size: 18), + label: Text(context.l10n.playAllCount(tracks.length)), style: FilledButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black87, @@ -885,65 +882,15 @@ class _LibraryTracksFolderScreenState }); } - void _downloadAll(BuildContext context, List tracks) { + void _playAll(List tracks) { if (tracks.isEmpty) return; - showDialog( - context: context, - builder: (dialogContext) { - final colorScheme = Theme.of(dialogContext).colorScheme; - return AlertDialog( - backgroundColor: colorScheme.surfaceContainerHigh, - title: const Text('Download All'), - content: Text('Download ${tracks.length} tracks?'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(dialogContext), - child: Text(context.l10n.dialogCancel), - ), - FilledButton( - onPressed: () { - Navigator.pop(dialogContext); - _executeDownloadAll(context, tracks); - }, - child: const Text('Download'), - ), - ], - ); - }, - ); - } - - void _executeDownloadAll(BuildContext context, List tracks) { - final settings = ref.read(settingsProvider); - if (settings.askQualityBeforeDownload) { - DownloadServicePicker.show( - context, - trackName: '${tracks.length} tracks', - artistName: '', - onSelect: (quality, service) { - ref - .read(downloadQueueProvider.notifier) - .addMultipleToQueue(tracks, service, qualityOverride: quality); - if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.snackbarAddedTracksToQueue(tracks.length), - ), - ), - ); - }, + final messenger = ScaffoldMessenger.of(context); + ref.read(playbackProvider.notifier).playTrackList(tracks).catchError((e) { + if (!mounted) return; + messenger.showSnackBar( + SnackBar(content: Text('Cannot play local tracks: $e')), ); - } else { - ref - .read(downloadQueueProvider.notifier) - .addMultipleToQueue(tracks, settings.defaultService); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.snackbarAddedTracksToQueue(tracks.length)), - ), - ); - } + }); } void _showCoverOptionsSheet(BuildContext context, bool hasCustomCover) { diff --git a/lib/screens/playlist_screen.dart b/lib/screens/playlist_screen.dart index ac9a3f76..8d43dc90 100644 --- a/lib/screens/playlist_screen.dart +++ b/lib/screens/playlist_screen.dart @@ -10,8 +10,8 @@ import 'package:spotiflac_android/providers/library_collections_provider.dart'; import 'package:spotiflac_android/utils/file_access.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/providers/local_library_provider.dart'; -import 'package:spotiflac_android/providers/playback_provider.dart'; import 'package:spotiflac_android/widgets/download_service_picker.dart'; +import 'package:spotiflac_android/widgets/playlist_picker_sheet.dart'; import 'package:spotiflac_android/widgets/track_collection_quick_actions.dart'; class PlaylistScreen extends ConsumerStatefulWidget { @@ -308,7 +308,7 @@ class _PlaylistScreenState extends ConsumerState { const SizedBox(width: 12), _buildDownloadAllCenterButton(context), const SizedBox(width: 12), - _buildShufflePlayButton(), + _buildAddToPlaylistButton(context), ], ), ], @@ -505,26 +505,16 @@ class _PlaylistScreenState extends ConsumerState { ); } - Widget _buildShufflePlayButton() { + Widget _buildAddToPlaylistButton(BuildContext context) { return _buildCircleButton( - icon: Icons.shuffle_rounded, - tooltip: 'Shuffle Play', - onPressed: _tracks.isEmpty ? null : _shufflePlayLocal, + icon: Icons.playlist_add, + tooltip: 'Add to Playlist', + onPressed: _tracks.isEmpty + ? null + : () => showAddTracksToPlaylistSheet(context, ref, _tracks), ); } - void _shufflePlayLocal() { - if (_tracks.isEmpty) return; - final shuffled = [..._tracks]..shuffle(); - final messenger = ScaffoldMessenger.of(context); - ref.read(playbackProvider.notifier).playTrackList(shuffled).catchError((e) { - if (!mounted) return; - messenger.showSnackBar( - SnackBar(content: Text('Cannot shuffle play local tracks: $e')), - ); - }); - } - void _confirmDownloadAll(BuildContext context) { if (_tracks.isEmpty) return; showDialog( @@ -717,7 +707,7 @@ class _PlaylistTrackItem extends ConsumerWidget { style: TextStyle(color: colorScheme.onSurfaceVariant), ), ), - if (isInLocalLibrary) ...[ + if (isInLocalLibrary || isInHistory) ...[ const SizedBox(width: 6), Container( padding: const EdgeInsets.symmetric( diff --git a/lib/screens/setup_screen.dart b/lib/screens/setup_screen.dart index 8ec82457..77456bd8 100644 --- a/lib/screens/setup_screen.dart +++ b/lib/screens/setup_screen.dart @@ -31,11 +31,7 @@ class _SetupScreenState extends ConsumerState { bool _isLoading = false; int _androidSdkVersion = 0; - // Mode selection - String _selectedMode = 'downloader'; - - // We add 1 for the Welcome step - int get _totalSteps => (_androidSdkVersion >= 33 ? 4 : 3) + 1; + int get _totalSteps => _androidSdkVersion >= 33 ? 4 : 3; @override void initState() { @@ -466,8 +462,6 @@ class _SetupScreenState extends ConsumerState { return _notificationPermissionGranted; case 2: return _selectedDirectory != null; - case 3: - return true; // Mode selection always has a default } } else { switch (logicStep) { @@ -475,8 +469,6 @@ class _SetupScreenState extends ConsumerState { return _storagePermissionGranted; case 1: return _selectedDirectory != null; - case 2: - return true; // Mode selection always has a default } } return false; @@ -553,7 +545,6 @@ class _SetupScreenState extends ConsumerState { if (_androidSdkVersion >= 33) _buildNotificationStep(colorScheme), _buildDirectoryStep(colorScheme), - _buildModeSelectionStep(colorScheme), ], ), ), @@ -747,38 +738,6 @@ class _SetupScreenState extends ConsumerState { ), ); } - - Widget _buildModeSelectionStep(ColorScheme colorScheme) { - return _StepLayout( - title: context.l10n.setupModeSelectionTitle, - description: context.l10n.setupModeSelectionDescription, - icon: Icons.tune, - child: Column( - children: [ - _ModeCard( - icon: Icons.download, - title: context.l10n.setupModeDownloaderTitle, - features: [ - context.l10n.setupModeDownloaderFeature1, - context.l10n.setupModeDownloaderFeature2, - context.l10n.setupModeDownloaderFeature3, - ], - isSelected: _selectedMode == 'downloader', - onTap: () => setState(() => _selectedMode = 'downloader'), - colorScheme: colorScheme, - ), - const SizedBox(height: 16), - Text( - context.l10n.setupModeChangeableLater, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: colorScheme.onSurfaceVariant, - ), - textAlign: TextAlign.center, - ), - ], - ), - ); - } } class _StepLayout extends StatelessWidget { @@ -888,126 +847,3 @@ class _SuccessCard extends StatelessWidget { ); } } - -class _ModeCard extends StatelessWidget { - final IconData icon; - final String title; - final List features; - final bool isSelected; - final VoidCallback onTap; - final ColorScheme colorScheme; - - const _ModeCard({ - required this.icon, - required this.title, - required this.features, - required this.isSelected, - required this.onTap, - required this.colorScheme, - }); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - width: double.infinity, - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: isSelected - ? colorScheme.primaryContainer - : colorScheme.surfaceContainerHighest, - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: isSelected - ? colorScheme.primary - : colorScheme.outlineVariant, - width: isSelected ? 2 : 1, - ), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(top: 2), - child: Icon( - isSelected - ? Icons.radio_button_checked - : Icons.radio_button_unchecked, - size: 22, - color: isSelected - ? colorScheme.primary - : colorScheme.onSurfaceVariant, - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - icon, - size: 22, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - title, - style: Theme.of(context).textTheme.titleMedium - ?.copyWith( - fontWeight: FontWeight.bold, - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurface, - ), - ), - ), - ], - ), - const SizedBox(height: 8), - ...features.map( - (feature) => Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '\u2022 ', - style: TextStyle( - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - ), - ), - Expanded( - child: Text( - feature, - style: Theme.of(context).textTheme.bodySmall - ?.copyWith( - color: isSelected - ? colorScheme.onPrimaryContainer - : colorScheme.onSurfaceVariant, - height: 1.4, - ), - ), - ), - ], - ), - ), - ), - ], - ), - ), - ], - ), - ), - ); - } -}