added ShuffleAllButton
This commit is contained in:
parent
b7075aad27
commit
f866ef51c2
10 changed files with 159 additions and 83 deletions
|
@ -19,4 +19,5 @@ abstract class AudioRepository {
|
|||
Future<Either<Failure, void>> skipToNext();
|
||||
Future<Either<Failure, void>> skipToPrevious();
|
||||
Future<Either<Failure, void>> setShuffleMode(ShuffleMode shuffleMode);
|
||||
Future<Either<Failure, void>> shuffleAll();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../widgets/shuffle_all_button.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({Key key}) : super(key: key);
|
||||
|
||||
|
@ -13,7 +15,7 @@ class _HomePageState extends State<HomePage> {
|
|||
print('HomePage.build');
|
||||
return Container(
|
||||
child: const Center(
|
||||
child: Text('Home Page'),
|
||||
child: ShuffleAllButton(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:mucke/domain/entities/shuffle_mode.dart';
|
||||
|
||||
import '../../domain/entities/playback_state.dart';
|
||||
import '../../domain/entities/shuffle_mode.dart';
|
||||
import '../../domain/entities/song.dart';
|
||||
import '../../domain/repositories/audio_repository.dart';
|
||||
|
||||
|
@ -103,6 +103,10 @@ abstract class _AudioStore with Store {
|
|||
_audioRepository.setShuffleMode(shuffleMode);
|
||||
}
|
||||
|
||||
Future<void> shuffleAll() async {
|
||||
_audioRepository.shuffleAll();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> updateSong(Song streamValue) async {
|
||||
print('updateSong');
|
||||
|
|
|
@ -102,6 +102,21 @@ mixin _$AudioStore on _AudioStore, Store {
|
|||
});
|
||||
}
|
||||
|
||||
final _$shuffleModeStreamAtom = Atom(name: '_AudioStore.shuffleModeStream');
|
||||
|
||||
@override
|
||||
ObservableStream<ShuffleMode> get shuffleModeStream {
|
||||
_$shuffleModeStreamAtom.reportRead();
|
||||
return super.shuffleModeStream;
|
||||
}
|
||||
|
||||
@override
|
||||
set shuffleModeStream(ObservableStream<ShuffleMode> value) {
|
||||
_$shuffleModeStreamAtom.reportWrite(value, super.shuffleModeStream, () {
|
||||
super.shuffleModeStream = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$playSongAsyncAction = AsyncAction('_AudioStore.playSong');
|
||||
|
||||
@override
|
||||
|
@ -165,7 +180,8 @@ song: ${song},
|
|||
playbackStateStream: ${playbackStateStream},
|
||||
currentPositionStream: ${currentPositionStream},
|
||||
queueStream: ${queueStream},
|
||||
queueIndexStream: ${queueIndexStream}
|
||||
queueIndexStream: ${queueIndexStream},
|
||||
shuffleModeStream: ${shuffleModeStream}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
19
lib/presentation/widgets/shuffle_all_button.dart
Normal file
19
lib/presentation/widgets/shuffle_all_button.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../state/audio_store.dart';
|
||||
|
||||
class ShuffleAllButton extends StatelessWidget {
|
||||
const ShuffleAllButton({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AudioStore audioStore = Provider.of<AudioStore>(context);
|
||||
|
||||
return RaisedButton(
|
||||
child: const Text('Shuffle All'),
|
||||
onPressed: () => audioStore.shuffleAll(),
|
||||
color: Theme.of(context).accentColor,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -76,9 +76,11 @@ class AudioManagerImpl implements AudioManager {
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<ShuffleMode> get shuffleModeStream => _shuffleModeStream(AudioService.customEventStream.cast());
|
||||
Stream<ShuffleMode> get shuffleModeStream =>
|
||||
_shuffleModeStream(AudioService.customEventStream.cast());
|
||||
|
||||
Stream<ShuffleMode> _shuffleModeStream(Stream<Map<String, dynamic>> source) async* {
|
||||
Stream<ShuffleMode> _shuffleModeStream(
|
||||
Stream<Map<String, dynamic>> source) async* {
|
||||
if (_shuffleMode != null) {
|
||||
yield _shuffleMode;
|
||||
}
|
||||
|
@ -116,6 +118,7 @@ class AudioManagerImpl implements AudioManager {
|
|||
await AudioService.start(
|
||||
backgroundTaskEntrypoint: _backgroundTaskEntrypoint,
|
||||
androidEnableQueue: true,
|
||||
androidStopForegroundOnPause: true,
|
||||
);
|
||||
await AudioService.customAction(INIT);
|
||||
}
|
||||
|
@ -153,7 +156,7 @@ class AudioManagerImpl implements AudioManager {
|
|||
Duration updateTime;
|
||||
Duration statePosition;
|
||||
|
||||
// should this class get an init method for this?
|
||||
// TODO: should this class get an init method for this?
|
||||
_sourcePlaybackStateStream.listen((currentState) {
|
||||
state = currentState;
|
||||
updateTime = currentState?.updateTime;
|
||||
|
@ -175,6 +178,12 @@ class AudioManagerImpl implements AudioManager {
|
|||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> shuffleAll() async {
|
||||
await _startAudioService();
|
||||
await AudioService.customAction(SHUFFLE_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
void _backgroundTaskEntrypoint() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:mucke/domain/entities/shuffle_mode.dart';
|
||||
|
||||
import '../../domain/entities/playback_state.dart';
|
||||
import '../../domain/entities/shuffle_mode.dart';
|
||||
import '../models/song_model.dart';
|
||||
|
||||
abstract class AudioManager {
|
||||
|
@ -19,4 +18,5 @@ abstract class AudioManager {
|
|||
Future<void> skipToNext();
|
||||
Future<void> skipToPrevious();
|
||||
Future<void> setShuffleMode(ShuffleMode shuffleMode);
|
||||
Future<void> shuffleAll();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
|
@ -7,12 +8,14 @@ import 'package:moor/isolate.dart';
|
|||
import 'package:moor/moor.dart';
|
||||
|
||||
import '../../domain/entities/shuffle_mode.dart';
|
||||
import '../models/song_model.dart';
|
||||
import 'moor_music_data_source.dart';
|
||||
|
||||
const String INIT = 'INIT';
|
||||
const String PLAY_WITH_CONTEXT = 'PLAY_WITH_CONTEXT';
|
||||
const String APP_LIFECYCLE_RESUMED = 'APP_LIFECYCLE_RESUMED';
|
||||
const String SET_SHUFFLE_MODE = 'SET_SHUFFLE_MODE';
|
||||
const String SHUFFLE_ALL = 'SHUFFLE_ALL';
|
||||
|
||||
const String KEY_INDEX = 'INDEX';
|
||||
|
||||
|
@ -25,13 +28,18 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
List<MediaItem> _playbackContext = <MediaItem>[];
|
||||
|
||||
ShuffleMode _shuffleMode = ShuffleMode.none;
|
||||
ShuffleMode get shuffleMode => _shuffleMode;
|
||||
set shuffleMode(ShuffleMode s) {
|
||||
_shuffleMode = s;
|
||||
AudioServiceBackground.sendCustomEvent({SET_SHUFFLE_MODE: s.toString()});
|
||||
}
|
||||
|
||||
int _index = -1;
|
||||
int get playbackIndex => _index;
|
||||
int _playbackIndex = -1;
|
||||
int get playbackIndex => _playbackIndex;
|
||||
set playbackIndex(int i) {
|
||||
print('setting index');
|
||||
_index = i;
|
||||
AudioServiceBackground.sendCustomEvent({KEY_INDEX: _index});
|
||||
_playbackIndex = i;
|
||||
AudioServiceBackground.sendCustomEvent({KEY_INDEX: _playbackIndex});
|
||||
}
|
||||
|
||||
Duration _position;
|
||||
|
@ -50,7 +58,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
@override
|
||||
Future<void> onPlayFromMediaId(String mediaId) async {
|
||||
AudioServiceBackground.setState(
|
||||
controls: [pauseControl, stopControl],
|
||||
controls: [MediaControl.pause, MediaControl.skipToNext],
|
||||
playing: true,
|
||||
processingState: AudioProcessingState.ready,
|
||||
);
|
||||
|
@ -64,7 +72,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
@override
|
||||
Future<void> onPlay() async {
|
||||
AudioServiceBackground.setState(
|
||||
controls: [pauseControl, stopControl],
|
||||
controls: [MediaControl.pause, MediaControl.skipToNext],
|
||||
processingState: AudioProcessingState.ready,
|
||||
updateTime: Duration(milliseconds: DateTime.now().millisecondsSinceEpoch),
|
||||
position: _position,
|
||||
|
@ -76,7 +84,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
@override
|
||||
Future<void> onPause() async {
|
||||
AudioServiceBackground.setState(
|
||||
controls: [playControl, stopControl],
|
||||
controls: [MediaControl.play, MediaControl.skipToNext],
|
||||
processingState: AudioProcessingState.ready,
|
||||
updateTime: Duration(milliseconds: DateTime.now().millisecondsSinceEpoch),
|
||||
position: _position,
|
||||
|
@ -116,6 +124,8 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
return _onAppLifecycleResumed();
|
||||
case SET_SHUFFLE_MODE:
|
||||
return _setShuffleMode((arguments as String).toShuffleMode());
|
||||
case SHUFFLE_ALL:
|
||||
return shuffleAll();
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +145,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
final _mediaItems = await _getMediaItemsFromPaths(context);
|
||||
final permutation = _generateSongPermutation(_mediaItems.length, index);
|
||||
_playbackContext = _getPermutatedSongs(_mediaItems, permutation);
|
||||
if (_shuffleMode == ShuffleMode.none)
|
||||
if (shuffleMode == ShuffleMode.none)
|
||||
playbackIndex = index;
|
||||
else
|
||||
playbackIndex = 0;
|
||||
|
@ -149,21 +159,40 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
}
|
||||
|
||||
Future<void> _setShuffleMode(ShuffleMode mode) async {
|
||||
_shuffleMode = mode;
|
||||
AudioServiceBackground.sendCustomEvent({SET_SHUFFLE_MODE: _shuffleMode.toString()});
|
||||
shuffleMode = mode;
|
||||
// TODO: adapt queue
|
||||
}
|
||||
|
||||
// TODO: pasted code -> reformat!
|
||||
Future<void> shuffleAll() async {
|
||||
final start = DateTime.now();
|
||||
shuffleMode = ShuffleMode.standard;
|
||||
final List<SongModel> songs = await _moorMusicDataSource.getSongs();
|
||||
final mediaItems = <MediaItem>[];
|
||||
for (final song in songs) {
|
||||
mediaItems.add(song.toMediaItem());
|
||||
}
|
||||
final rng = Random();
|
||||
final index = rng.nextInt(mediaItems.length);
|
||||
|
||||
final permutation = _generateSongPermutation(mediaItems.length, index);
|
||||
_playbackContext = _getPermutatedSongs(mediaItems, permutation);
|
||||
playbackIndex = 0;
|
||||
AudioServiceBackground.setQueue(_playbackContext);
|
||||
final end = DateTime.now();
|
||||
print(end.difference(start).inMilliseconds);
|
||||
_startPlayback(playbackIndex);
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
// TODO: optimize -> too slow for whole library
|
||||
// fetching all songs together and preparing playback takes ~500ms compared to ~10.000ms individually
|
||||
Future<List<MediaItem>> _getMediaItemsFromPaths(List<String> paths) async {
|
||||
final mediaItems = <MediaItem>[];
|
||||
for (final path in paths) {
|
||||
final song = await _moorMusicDataSource.getSongByPath(path);
|
||||
mediaItems.add(song.toMediaItem());
|
||||
}
|
||||
// TODO: not good, side effects...
|
||||
_originalPlaybackContext = mediaItems;
|
||||
|
||||
return mediaItems;
|
||||
}
|
||||
|
@ -173,7 +202,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
// permutation[i] = j; => song j is on the i-th position in the permutated list
|
||||
List<int> permutation;
|
||||
|
||||
switch (_shuffleMode) {
|
||||
switch (shuffleMode) {
|
||||
case ShuffleMode.none:
|
||||
permutation = List<int>.generate(length, (i) => i);
|
||||
break;
|
||||
|
@ -200,7 +229,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
Future<void> _startPlayback(int index) async {
|
||||
// TODO: DRY
|
||||
AudioServiceBackground.setState(
|
||||
controls: [pauseControl, stopControl],
|
||||
controls: [MediaControl.pause, MediaControl.skipToNext],
|
||||
playing: true,
|
||||
processingState: AudioProcessingState.ready,
|
||||
);
|
||||
|
@ -232,19 +261,3 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MediaControl playControl = const MediaControl(
|
||||
androidIcon: 'drawable/ic_action_play_arrow',
|
||||
label: 'Play',
|
||||
action: MediaAction.play,
|
||||
);
|
||||
MediaControl pauseControl = const MediaControl(
|
||||
androidIcon: 'drawable/ic_action_pause',
|
||||
label: 'Pause',
|
||||
action: MediaAction.pause,
|
||||
);
|
||||
MediaControl stopControl = const MediaControl(
|
||||
androidIcon: 'drawable/ic_action_stop',
|
||||
label: 'Stop',
|
||||
action: MediaAction.stop,
|
||||
);
|
||||
|
|
|
@ -73,4 +73,10 @@ class AudioRepositoryImpl implements AudioRepository {
|
|||
await _audioManager.setShuffleMode(shuffleMode);
|
||||
return const Right(null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> shuffleAll() async {
|
||||
await _audioManager.shuffleAll();
|
||||
return const Right(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,58 @@
|
|||
// Helper file to make coverage work for all dart files\n
|
||||
// ignore_for_file: unused_import
|
||||
import 'package:mucke/domain/entities/album.dart';
|
||||
import 'package:mucke/domain/entities/song.dart';
|
||||
import 'package:mucke/domain/entities/playback_state.dart';
|
||||
import 'package:mucke/domain/entities/artist.dart';
|
||||
import 'package:mucke/domain/repositories/music_data_repository.dart';
|
||||
import 'package:mucke/domain/repositories/audio_repository.dart';
|
||||
import 'package:mucke/main.dart';
|
||||
import 'package:mucke/core/usecase.dart';
|
||||
import 'package:mucke/core/error/failures.dart';
|
||||
import 'package:mucke/presentation/state/audio_store.dart';
|
||||
import 'package:mucke/presentation/state/navigation_store.dart';
|
||||
import 'package:mucke/presentation/state/music_data_store.dart';
|
||||
import 'package:mucke/presentation/utils.dart';
|
||||
import 'package:mucke/presentation/theming.dart';
|
||||
import 'package:mucke/presentation/pages/home_page.dart';
|
||||
import 'package:mucke/presentation/pages/songs_page.dart';
|
||||
import 'package:mucke/presentation/pages/currently_playing.dart';
|
||||
import 'package:mucke/presentation/pages/albums_page.dart';
|
||||
import 'package:mucke/presentation/pages/library_tab_container.dart';
|
||||
import 'package:mucke/presentation/pages/settings_page.dart';
|
||||
import 'package:mucke/presentation/pages/album_details_page.dart';
|
||||
import 'package:mucke/presentation/pages/library_page.dart';
|
||||
import 'package:mucke/presentation/widgets/next_song.dart';
|
||||
import 'package:mucke/presentation/widgets/currently_playing_bar.dart';
|
||||
import 'package:mucke/presentation/widgets/queue_card.dart';
|
||||
import 'package:mucke/presentation/widgets/injection_widget.dart';
|
||||
import 'package:mucke/presentation/widgets/play_pause_button.dart';
|
||||
import 'package:mucke/presentation/widgets/album_art.dart';
|
||||
import 'package:mucke/presentation/widgets/next_indicator.dart';
|
||||
import 'package:mucke/presentation/widgets/next_button.dart';
|
||||
import 'package:mucke/presentation/widgets/navbar.dart';
|
||||
import 'package:mucke/presentation/widgets/time_progress_indicator.dart';
|
||||
import 'package:mucke/presentation/widgets/audio_service_widget.dart';
|
||||
import 'package:mucke/presentation/widgets/album_art_list_tile.dart';
|
||||
import 'package:mucke/presentation/widgets/previous_button.dart';
|
||||
import 'package:mucke/presentation/widgets/playback_control.dart';
|
||||
import 'package:mucke/system/datasources/music_data_source_contract.dart';
|
||||
import 'package:mucke/system/datasources/local_music_fetcher.dart';
|
||||
import 'package:mucke/system/datasources/audio_manager.dart';
|
||||
import 'package:mucke/system/datasources/audio_player_task.dart';
|
||||
import 'package:mucke/system/datasources/moor_music_data_source.dart';
|
||||
import 'package:mucke/system/datasources/local_music_fetcher_contract.dart';
|
||||
import 'package:mucke/system/datasources/audio_manager_contract.dart';
|
||||
import 'package:mucke/injection_container.dart';
|
||||
import 'package:mucke/system/repositories/audio_repository_impl.dart';
|
||||
import 'package:mucke/system/repositories/music_data_repository_impl.dart';
|
||||
import 'package:mucke/system/models/playback_state_model.dart';
|
||||
import 'package:mucke/system/datasources/audio_manager_contract.dart';
|
||||
import 'package:mucke/system/datasources/local_music_fetcher.dart';
|
||||
import 'package:mucke/system/datasources/audio_player_task.dart';
|
||||
import 'package:mucke/system/datasources/local_music_fetcher_contract.dart';
|
||||
import 'package:mucke/system/datasources/audio_manager.dart';
|
||||
import 'package:mucke/system/datasources/moor_music_data_source.dart';
|
||||
import 'package:mucke/system/datasources/music_data_source_contract.dart';
|
||||
import 'package:mucke/system/models/artist_model.dart';
|
||||
import 'package:mucke/system/models/album_model.dart';
|
||||
import 'package:mucke/system/models/song_model.dart';
|
||||
import 'package:mucke/injection_container.dart';
|
||||
import 'package:mucke/system/models/playback_state_model.dart';
|
||||
import 'package:mucke/main.dart';
|
||||
import 'package:mucke/presentation/state/music_data_store.dart';
|
||||
import 'package:mucke/presentation/state/navigation_store.dart';
|
||||
import 'package:mucke/presentation/state/audio_store.dart';
|
||||
import 'package:mucke/presentation/widgets/next_indicator.dart';
|
||||
import 'package:mucke/presentation/widgets/audio_service_widget.dart';
|
||||
import 'package:mucke/presentation/widgets/previous_button.dart';
|
||||
import 'package:mucke/presentation/widgets/play_pause_button.dart';
|
||||
import 'package:mucke/presentation/widgets/album_art.dart';
|
||||
import 'package:mucke/presentation/widgets/shuffle_all_button.dart';
|
||||
import 'package:mucke/presentation/widgets/shuffle_button.dart';
|
||||
import 'package:mucke/presentation/widgets/injection_widget.dart';
|
||||
import 'package:mucke/presentation/widgets/next_song.dart';
|
||||
import 'package:mucke/presentation/widgets/queue_card.dart';
|
||||
import 'package:mucke/presentation/widgets/playback_control.dart';
|
||||
import 'package:mucke/presentation/widgets/album_art_list_tile.dart';
|
||||
import 'package:mucke/presentation/widgets/navbar.dart';
|
||||
import 'package:mucke/presentation/widgets/time_progress_indicator.dart';
|
||||
import 'package:mucke/presentation/widgets/next_button.dart';
|
||||
import 'package:mucke/presentation/widgets/currently_playing_bar.dart';
|
||||
import 'package:mucke/presentation/theming.dart';
|
||||
import 'package:mucke/presentation/utils.dart';
|
||||
import 'package:mucke/presentation/pages/library_page.dart';
|
||||
import 'package:mucke/presentation/pages/settings_page.dart';
|
||||
import 'package:mucke/presentation/pages/library_tab_container.dart';
|
||||
import 'package:mucke/presentation/pages/songs_page.dart';
|
||||
import 'package:mucke/presentation/pages/queue_page.dart';
|
||||
import 'package:mucke/presentation/pages/home_page.dart';
|
||||
import 'package:mucke/presentation/pages/albums_page.dart';
|
||||
import 'package:mucke/presentation/pages/album_details_page.dart';
|
||||
import 'package:mucke/presentation/pages/currently_playing.dart';
|
||||
import 'package:mucke/presentation/pages/artists_page.dart';
|
||||
import 'package:mucke/core/usecase.dart';
|
||||
import 'package:mucke/core/error/failures.dart';
|
||||
import 'package:mucke/domain/repositories/audio_repository.dart';
|
||||
import 'package:mucke/domain/repositories/music_data_repository.dart';
|
||||
import 'package:mucke/domain/entities/shuffle_mode.dart';
|
||||
import 'package:mucke/domain/entities/album.dart';
|
||||
import 'package:mucke/domain/entities/artist.dart';
|
||||
import 'package:mucke/domain/entities/song.dart';
|
||||
import 'package:mucke/domain/entities/playback_state.dart';
|
||||
void main(){}
|
||||
|
|
Loading…
Add table
Reference in a new issue