diff --git a/lib/domain/entities/song.dart b/lib/domain/entities/song.dart index 0b48098..b6516ca 100644 --- a/lib/domain/entities/song.dart +++ b/lib/domain/entities/song.dart @@ -8,6 +8,7 @@ class Song extends Equatable { @required this.artist, @required this.path, @required this.duration, + @required this.blocked, this.trackNumber, this.albumArtPath, }); @@ -20,6 +21,8 @@ class Song extends Equatable { final int duration; final int trackNumber; final String albumArtPath; + /// Is this song blocked in shuffle mode? + final bool blocked; @override List get props => [title, album, artist]; diff --git a/lib/domain/repositories/music_data_repository.dart b/lib/domain/repositories/music_data_repository.dart index a4cf947..da4870a 100644 --- a/lib/domain/repositories/music_data_repository.dart +++ b/lib/domain/repositories/music_data_repository.dart @@ -11,4 +11,6 @@ abstract class MusicDataRepository { Future>> getAlbums(); Future>> getArtists(); Future updateDatabase(); + + Future setSongBlocked(Song song, bool blocked); } \ No newline at end of file diff --git a/lib/injection_container.dart b/lib/injection_container.dart index b4cb687..bb057e6 100644 --- a/lib/injection_container.dart +++ b/lib/injection_container.dart @@ -30,7 +30,6 @@ Future setupGetIt() async { final musicDataStore = MusicDataStore( musicDataRepository: getIt(), ); - musicDataStore.init(); return musicDataStore; }, ); @@ -39,14 +38,12 @@ Future setupGetIt() async { final audioStore = AudioStore( audioRepository: getIt(), ); - audioStore.init(); return audioStore; }, ); getIt.registerFactory( () { final navigationStore = NavigationStore(); - navigationStore.init(); return navigationStore; }, ); diff --git a/lib/presentation/pages/currently_playing.dart b/lib/presentation/pages/currently_playing.dart index 659bbff..0c319b2 100644 --- a/lib/presentation/pages/currently_playing.dart +++ b/lib/presentation/pages/currently_playing.dart @@ -4,10 +4,10 @@ import 'package:provider/provider.dart'; import '../../domain/entities/song.dart'; import '../state/audio_store.dart'; -import '../theming.dart'; import '../widgets/album_art.dart'; import '../widgets/next_indicator.dart'; import '../widgets/playback_control.dart'; +import '../widgets/song_customization_buttons.dart'; import '../widgets/time_progress_indicator.dart'; import 'queue_page.dart'; @@ -64,30 +64,7 @@ class CurrentlyPlayingPage extends StatelessWidget { const Spacer( flex: 4, ), - Row( - children: [ - const Icon( - Icons.link, - size: 20.0, - ), - Container( - width: 40, - ), - const Icon( - Icons.favorite, - size: 20.0, - color: RASPBERRY, - ), - Container( - width: 40, - ), - const Icon( - Icons.remove_circle_outline, - size: 20.0, - ), - ], - mainAxisAlignment: MainAxisAlignment.center, - ), + const SongCustomizationButtons(), const Spacer( flex: 3, ), diff --git a/lib/presentation/state/audio_store.dart b/lib/presentation/state/audio_store.dart index bbce8bb..b349db7 100644 --- a/lib/presentation/state/audio_store.dart +++ b/lib/presentation/state/audio_store.dart @@ -14,11 +14,28 @@ class AudioStore extends _AudioStore with _$AudioStore { } abstract class _AudioStore with Store { - _AudioStore(this._audioRepository); + _AudioStore(this._audioRepository) { + currentSong = _audioRepository.currentSongStream.asObservable(); + + currentPositionStream = + _audioRepository.currentPositionStream.asObservable(initialValue: 0); + + queueStream = _audioRepository.queueStream.asObservable(initialValue: []); + + queueIndexStream = _audioRepository.queueIndexStream.asObservable(); + + shuffleModeStream = _audioRepository.shuffleModeStream + .asObservable(initialValue: ShuffleMode.none); + + _disposers.add(autorun((_) { + updateSong(currentSong.value); + })); + + playbackStateStream = _audioRepository.playbackStateStream.asObservable(); + } final AudioRepository _audioRepository; - bool _initialized = false; final List _disposers = []; // TODO: naming and usage confusing! @@ -42,31 +59,6 @@ abstract class _AudioStore with Store { @observable ObservableStream shuffleModeStream; - @action - void init() { - if (!_initialized) { - print('AudioStore.init'); - currentSong = _audioRepository.currentSongStream.asObservable(); - - currentPositionStream = - _audioRepository.currentPositionStream.asObservable(initialValue: 0); - - queueStream = _audioRepository.queueStream.asObservable(initialValue: []); - - queueIndexStream = _audioRepository.queueIndexStream.asObservable(); - - shuffleModeStream = _audioRepository.shuffleModeStream.asObservable(initialValue: ShuffleMode.none); - - _disposers.add(autorun((_) { - updateSong(currentSong.value); - })); - - playbackStateStream = _audioRepository.playbackStateStream.asObservable(); - - _initialized = true; - } - } - void dispose() { print('AudioStore.dispose'); for (final ReactionDisposer d in _disposers) { diff --git a/lib/presentation/state/audio_store.g.dart b/lib/presentation/state/audio_store.g.dart index 8a5b23d..0770988 100644 --- a/lib/presentation/state/audio_store.g.dart +++ b/lib/presentation/state/audio_store.g.dart @@ -159,19 +159,6 @@ mixin _$AudioStore on _AudioStore, Store { return _$updateSongAsyncAction.run(() => super.updateSong(streamValue)); } - final _$_AudioStoreActionController = ActionController(name: '_AudioStore'); - - @override - void init() { - final _$actionInfo = - _$_AudioStoreActionController.startAction(name: '_AudioStore.init'); - try { - return super.init(); - } finally { - _$_AudioStoreActionController.endAction(_$actionInfo); - } - } - @override String toString() { return ''' diff --git a/lib/presentation/state/music_data_store.dart b/lib/presentation/state/music_data_store.dart index a7c2b71..9288e12 100644 --- a/lib/presentation/state/music_data_store.dart +++ b/lib/presentation/state/music_data_store.dart @@ -14,12 +14,14 @@ class MusicDataStore extends _MusicDataStore with _$MusicDataStore { } abstract class _MusicDataStore with Store { - _MusicDataStore(this._musicDataRepository); + _MusicDataStore(this._musicDataRepository) { + fetchArtists(); + fetchAlbums(); + fetchSongs(); + } final MusicDataRepository _musicDataRepository; - bool _initialized = false; - @observable ObservableList artists = [].asObservable(); @observable @@ -41,18 +43,6 @@ abstract class _MusicDataStore with Store { @observable ObservableList albumSongs = [].asObservable(); - @action - void init() { - if (!_initialized) { - print('MusicDataStore.init'); - fetchArtists(); - fetchAlbums(); - fetchSongs(); - - _initialized = true; - } - } - @action Future updateDatabase() async { isUpdatingDatabase = true; @@ -122,4 +112,8 @@ abstract class _MusicDataStore with Store { (songList) => albumSongs.addAll(songList), ); } + + Future setSongBlocked(Song song, bool blocked) async { + await _musicDataRepository.setSongBlocked(song, blocked); + } } diff --git a/lib/presentation/state/music_data_store.g.dart b/lib/presentation/state/music_data_store.g.dart index 41d8d2a..b95313e 100644 --- a/lib/presentation/state/music_data_store.g.dart +++ b/lib/presentation/state/music_data_store.g.dart @@ -169,20 +169,6 @@ mixin _$MusicDataStore on _MusicDataStore, Store { .run(() => super.fetchSongsFromAlbum(album)); } - final _$_MusicDataStoreActionController = - ActionController(name: '_MusicDataStore'); - - @override - void init() { - final _$actionInfo = _$_MusicDataStoreActionController.startAction( - name: '_MusicDataStore.init'); - try { - return super.init(); - } finally { - _$_MusicDataStoreActionController.endAction(_$actionInfo); - } - } - @override String toString() { return ''' diff --git a/lib/presentation/state/navigation_store.dart b/lib/presentation/state/navigation_store.dart index 1657598..b90e4b0 100644 --- a/lib/presentation/state/navigation_store.dart +++ b/lib/presentation/state/navigation_store.dart @@ -9,18 +9,8 @@ class NavigationStore extends _NavigationStore with _$NavigationStore { abstract class _NavigationStore with Store { _NavigationStore(); - bool _initialized = false; - @observable int navIndex = 1; - @action - void init() { - if (!_initialized) { - print('NavigationStore.init'); - _initialized = true; - } - } - @action void setNavIndex(int i) { navIndex = i; diff --git a/lib/presentation/state/navigation_store.g.dart b/lib/presentation/state/navigation_store.g.dart index f5feecd..ba6a058 100644 --- a/lib/presentation/state/navigation_store.g.dart +++ b/lib/presentation/state/navigation_store.g.dart @@ -27,17 +27,6 @@ mixin _$NavigationStore on _NavigationStore, Store { final _$_NavigationStoreActionController = ActionController(name: '_NavigationStore'); - @override - void init() { - final _$actionInfo = _$_NavigationStoreActionController.startAction( - name: '_NavigationStore.init'); - try { - return super.init(); - } finally { - _$_NavigationStoreActionController.endAction(_$actionInfo); - } - } - @override void setNavIndex(int i) { final _$actionInfo = _$_NavigationStoreActionController.startAction( diff --git a/lib/presentation/widgets/shuffle_button.dart b/lib/presentation/widgets/shuffle_button.dart index b9c34bf..f2e2d49 100644 --- a/lib/presentation/widgets/shuffle_button.dart +++ b/lib/presentation/widgets/shuffle_button.dart @@ -16,41 +16,44 @@ class ShuffleButton extends StatelessWidget { return Observer( builder: (BuildContext context) { - switch (audioStore.shuffleModeStream.value) { - case ShuffleMode.none: - return IconButton( - icon: const Icon( - Icons.shuffle, - color: Colors.white24, - ), - iconSize: iconSize, - onPressed: () { - audioStore.setShuffleMode(ShuffleMode.standard); - }, - ); - case ShuffleMode.standard: - return IconButton( - icon: const Icon( - Icons.shuffle, - color: Colors.white, - ), - iconSize: iconSize, - onPressed: () { - audioStore.setShuffleMode(ShuffleMode.none); - }, - ); - default: - return IconButton( - icon: const Icon( - Icons.shuffle, - color: Colors.blue, - ), - iconSize: iconSize, - onPressed: () { - audioStore.setShuffleMode(ShuffleMode.none); - }, - ); + if (audioStore.shuffleModeStream != null) { + switch (audioStore.shuffleModeStream.value) { + case ShuffleMode.none: + return IconButton( + icon: const Icon( + Icons.shuffle, + color: Colors.white24, + ), + iconSize: iconSize, + onPressed: () { + audioStore.setShuffleMode(ShuffleMode.standard); + }, + ); + case ShuffleMode.standard: + return IconButton( + icon: const Icon( + Icons.shuffle, + color: Colors.white, + ), + iconSize: iconSize, + onPressed: () { + audioStore.setShuffleMode(ShuffleMode.plus); + }, + ); + case ShuffleMode.plus: + return IconButton( + icon: const Icon( + Icons.fingerprint, + color: Colors.white, + ), + iconSize: iconSize, + onPressed: () { + audioStore.setShuffleMode(ShuffleMode.none); + }, + ); + } } + return Container(); }, ); } diff --git a/lib/presentation/widgets/song_customization_buttons.dart b/lib/presentation/widgets/song_customization_buttons.dart new file mode 100644 index 0000000..bccf8d8 --- /dev/null +++ b/lib/presentation/widgets/song_customization_buttons.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:provider/provider.dart'; + +import '../state/audio_store.dart'; +import '../state/music_data_store.dart'; +import '../theming.dart'; + +class SongCustomizationButtons extends StatelessWidget { + const SongCustomizationButtons({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final MusicDataStore musicDataStore = Provider.of(context); + final AudioStore audioStore = Provider.of(context); + + return Observer( + builder: (BuildContext context) => Row( + children: [ + const Icon( + Icons.link, + size: 20.0, + ), + Container( + width: 40, + ), + const Icon( + Icons.favorite, + size: 20.0, + color: RASPBERRY, + ), + Container( + width: 40, + ), + IconButton( + icon: Icon( + Icons.remove_circle_outline, + size: 20.0, + color: audioStore.song.blocked ? Colors.red : Colors.white70, + ), + onPressed: () => musicDataStore.setSongBlocked(audioStore.song, !audioStore.song.blocked), + ), + ], + mainAxisAlignment: MainAxisAlignment.center, + ), + ); + } +} diff --git a/lib/system/datasources/audio_manager.dart b/lib/system/datasources/audio_manager.dart index 1bdfbc4..cf6b34f 100644 --- a/lib/system/datasources/audio_manager.dart +++ b/lib/system/datasources/audio_manager.dart @@ -105,6 +105,7 @@ class AudioManagerImpl implements AudioManager { @override Future play() async { + await _startAudioService(); await AudioService.play(); } diff --git a/lib/system/datasources/audio_player_task.dart b/lib/system/datasources/audio_player_task.dart index 33797c7..611d4a8 100644 --- a/lib/system/datasources/audio_player_task.dart +++ b/lib/system/datasources/audio_player_task.dart @@ -66,6 +66,13 @@ class AudioPlayerTask extends BackgroundAudioTask { await super.onStop(); } + // @override + // Future onClose() async { + // audioPlayer.stop(); + // AudioServiceBackground.setState(controls: null, processingState: null, playing: false); + // AudioServiceBackground.setMediaItem(null); + // } + @override Future onPlay() async { audioPlayer.play(); @@ -152,7 +159,7 @@ class AudioPlayerTask extends BackgroundAudioTask { Future playPlaylist(List mediaItems, int index) async { final permutation = - qm.generatePermutation(shuffleMode, mediaItems.length, index); + qm.generatePermutation(shuffleMode, mediaItems, index); playbackContext = qm.getPermutatedSongs(mediaItems, permutation); originalPlaybackContext = mediaItems; diff --git a/lib/system/datasources/moor_music_data_source.dart b/lib/system/datasources/moor_music_data_source.dart index aee2d12..21edca8 100644 --- a/lib/system/datasources/moor_music_data_source.dart +++ b/lib/system/datasources/moor_music_data_source.dart @@ -31,7 +31,6 @@ class Albums extends Table { TextColumn get artist => text()(); TextColumn get albumArtPath => text().nullable()(); IntColumn get year => integer().nullable()(); - BoolColumn get present => boolean().withDefault(const Constant(true))(); } @DataClassName('MoorSong') @@ -44,7 +43,7 @@ class Songs extends Table { IntColumn get duration => integer().nullable()(); TextColumn get albumArtPath => text().nullable()(); IntColumn get trackNumber => integer().nullable()(); - BoolColumn get present => boolean().withDefault(const Constant(true))(); + BoolColumn get blocked => boolean().withDefault(const Constant(false))(); @override Set get primaryKey => {path}; diff --git a/lib/system/datasources/moor_music_data_source.g.dart b/lib/system/datasources/moor_music_data_source.g.dart index cb970c1..04de88d 100644 --- a/lib/system/datasources/moor_music_data_source.g.dart +++ b/lib/system/datasources/moor_music_data_source.g.dart @@ -161,20 +161,17 @@ class MoorAlbum extends DataClass implements Insertable { final String artist; final String albumArtPath; final int year; - final bool present; MoorAlbum( {@required this.id, @required this.title, @required this.artist, this.albumArtPath, - this.year, - @required this.present}); + this.year}); factory MoorAlbum.fromData(Map data, GeneratedDatabase db, {String prefix}) { final effectivePrefix = prefix ?? ''; final intType = db.typeSystem.forDartType(); final stringType = db.typeSystem.forDartType(); - final boolType = db.typeSystem.forDartType(); return MoorAlbum( id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']), title: @@ -184,8 +181,6 @@ class MoorAlbum extends DataClass implements Insertable { albumArtPath: stringType .mapFromDatabaseResponse(data['${effectivePrefix}album_art_path']), year: intType.mapFromDatabaseResponse(data['${effectivePrefix}year']), - present: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}present']), ); } @override @@ -206,9 +201,6 @@ class MoorAlbum extends DataClass implements Insertable { if (!nullToAbsent || year != null) { map['year'] = Variable(year); } - if (!nullToAbsent || present != null) { - map['present'] = Variable(present); - } return map; } @@ -223,9 +215,6 @@ class MoorAlbum extends DataClass implements Insertable { ? const Value.absent() : Value(albumArtPath), year: year == null && nullToAbsent ? const Value.absent() : Value(year), - present: present == null && nullToAbsent - ? const Value.absent() - : Value(present), ); } @@ -238,7 +227,6 @@ class MoorAlbum extends DataClass implements Insertable { artist: serializer.fromJson(json['artist']), albumArtPath: serializer.fromJson(json['albumArtPath']), year: serializer.fromJson(json['year']), - present: serializer.fromJson(json['present']), ); } @override @@ -250,7 +238,6 @@ class MoorAlbum extends DataClass implements Insertable { 'artist': serializer.toJson(artist), 'albumArtPath': serializer.toJson(albumArtPath), 'year': serializer.toJson(year), - 'present': serializer.toJson(present), }; } @@ -259,15 +246,13 @@ class MoorAlbum extends DataClass implements Insertable { String title, String artist, String albumArtPath, - int year, - bool present}) => + int year}) => MoorAlbum( id: id ?? this.id, title: title ?? this.title, artist: artist ?? this.artist, albumArtPath: albumArtPath ?? this.albumArtPath, year: year ?? this.year, - present: present ?? this.present, ); @override String toString() { @@ -276,8 +261,7 @@ class MoorAlbum extends DataClass implements Insertable { ..write('title: $title, ') ..write('artist: $artist, ') ..write('albumArtPath: $albumArtPath, ') - ..write('year: $year, ') - ..write('present: $present') + ..write('year: $year') ..write(')')) .toString(); } @@ -288,9 +272,7 @@ class MoorAlbum extends DataClass implements Insertable { $mrjc( title.hashCode, $mrjc( - artist.hashCode, - $mrjc(albumArtPath.hashCode, - $mrjc(year.hashCode, present.hashCode)))))); + artist.hashCode, $mrjc(albumArtPath.hashCode, year.hashCode))))); @override bool operator ==(dynamic other) => identical(this, other) || @@ -299,8 +281,7 @@ class MoorAlbum extends DataClass implements Insertable { other.title == this.title && other.artist == this.artist && other.albumArtPath == this.albumArtPath && - other.year == this.year && - other.present == this.present); + other.year == this.year); } class AlbumsCompanion extends UpdateCompanion { @@ -309,14 +290,12 @@ class AlbumsCompanion extends UpdateCompanion { final Value artist; final Value albumArtPath; final Value year; - final Value present; const AlbumsCompanion({ this.id = const Value.absent(), this.title = const Value.absent(), this.artist = const Value.absent(), this.albumArtPath = const Value.absent(), this.year = const Value.absent(), - this.present = const Value.absent(), }); AlbumsCompanion.insert({ this.id = const Value.absent(), @@ -324,7 +303,6 @@ class AlbumsCompanion extends UpdateCompanion { @required String artist, this.albumArtPath = const Value.absent(), this.year = const Value.absent(), - this.present = const Value.absent(), }) : title = Value(title), artist = Value(artist); static Insertable custom({ @@ -333,7 +311,6 @@ class AlbumsCompanion extends UpdateCompanion { Expression artist, Expression albumArtPath, Expression year, - Expression present, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -341,7 +318,6 @@ class AlbumsCompanion extends UpdateCompanion { if (artist != null) 'artist': artist, if (albumArtPath != null) 'album_art_path': albumArtPath, if (year != null) 'year': year, - if (present != null) 'present': present, }); } @@ -350,15 +326,13 @@ class AlbumsCompanion extends UpdateCompanion { Value title, Value artist, Value albumArtPath, - Value year, - Value present}) { + Value year}) { return AlbumsCompanion( id: id ?? this.id, title: title ?? this.title, artist: artist ?? this.artist, albumArtPath: albumArtPath ?? this.albumArtPath, year: year ?? this.year, - present: present ?? this.present, ); } @@ -380,9 +354,6 @@ class AlbumsCompanion extends UpdateCompanion { if (year.present) { map['year'] = Variable(year.value); } - if (present.present) { - map['present'] = Variable(present.value); - } return map; } @@ -393,8 +364,7 @@ class AlbumsCompanion extends UpdateCompanion { ..write('title: $title, ') ..write('artist: $artist, ') ..write('albumArtPath: $albumArtPath, ') - ..write('year: $year, ') - ..write('present: $present') + ..write('year: $year') ..write(')')) .toString(); } @@ -463,18 +433,8 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> { ); } - final VerificationMeta _presentMeta = const VerificationMeta('present'); - GeneratedBoolColumn _present; @override - GeneratedBoolColumn get present => _present ??= _constructPresent(); - GeneratedBoolColumn _constructPresent() { - return GeneratedBoolColumn('present', $tableName, false, - defaultValue: const Constant(true)); - } - - @override - List get $columns => - [id, title, artist, albumArtPath, year, present]; + List get $columns => [id, title, artist, albumArtPath, year]; @override $AlbumsTable get asDslTable => this; @override @@ -511,10 +471,6 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, MoorAlbum> { context.handle( _yearMeta, year.isAcceptableOrUnknown(data['year'], _yearMeta)); } - if (data.containsKey('present')) { - context.handle(_presentMeta, - present.isAcceptableOrUnknown(data['present'], _presentMeta)); - } return context; } @@ -541,7 +497,7 @@ class MoorSong extends DataClass implements Insertable { final int duration; final String albumArtPath; final int trackNumber; - final bool present; + final bool blocked; MoorSong( {@required this.title, @required this.albumTitle, @@ -551,7 +507,7 @@ class MoorSong extends DataClass implements Insertable { this.duration, this.albumArtPath, this.trackNumber, - @required this.present}); + @required this.blocked}); factory MoorSong.fromData(Map data, GeneratedDatabase db, {String prefix}) { final effectivePrefix = prefix ?? ''; @@ -574,8 +530,8 @@ class MoorSong extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}album_art_path']), trackNumber: intType .mapFromDatabaseResponse(data['${effectivePrefix}track_number']), - present: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}present']), + blocked: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}blocked']), ); } @override @@ -605,8 +561,8 @@ class MoorSong extends DataClass implements Insertable { if (!nullToAbsent || trackNumber != null) { map['track_number'] = Variable(trackNumber); } - if (!nullToAbsent || present != null) { - map['present'] = Variable(present); + if (!nullToAbsent || blocked != null) { + map['blocked'] = Variable(blocked); } return map; } @@ -633,9 +589,9 @@ class MoorSong extends DataClass implements Insertable { trackNumber: trackNumber == null && nullToAbsent ? const Value.absent() : Value(trackNumber), - present: present == null && nullToAbsent + blocked: blocked == null && nullToAbsent ? const Value.absent() - : Value(present), + : Value(blocked), ); } @@ -651,7 +607,7 @@ class MoorSong extends DataClass implements Insertable { duration: serializer.fromJson(json['duration']), albumArtPath: serializer.fromJson(json['albumArtPath']), trackNumber: serializer.fromJson(json['trackNumber']), - present: serializer.fromJson(json['present']), + blocked: serializer.fromJson(json['blocked']), ); } @override @@ -666,7 +622,7 @@ class MoorSong extends DataClass implements Insertable { 'duration': serializer.toJson(duration), 'albumArtPath': serializer.toJson(albumArtPath), 'trackNumber': serializer.toJson(trackNumber), - 'present': serializer.toJson(present), + 'blocked': serializer.toJson(blocked), }; } @@ -679,7 +635,7 @@ class MoorSong extends DataClass implements Insertable { int duration, String albumArtPath, int trackNumber, - bool present}) => + bool blocked}) => MoorSong( title: title ?? this.title, albumTitle: albumTitle ?? this.albumTitle, @@ -689,7 +645,7 @@ class MoorSong extends DataClass implements Insertable { duration: duration ?? this.duration, albumArtPath: albumArtPath ?? this.albumArtPath, trackNumber: trackNumber ?? this.trackNumber, - present: present ?? this.present, + blocked: blocked ?? this.blocked, ); @override String toString() { @@ -702,7 +658,7 @@ class MoorSong extends DataClass implements Insertable { ..write('duration: $duration, ') ..write('albumArtPath: $albumArtPath, ') ..write('trackNumber: $trackNumber, ') - ..write('present: $present') + ..write('blocked: $blocked') ..write(')')) .toString(); } @@ -723,7 +679,7 @@ class MoorSong extends DataClass implements Insertable { $mrjc( albumArtPath.hashCode, $mrjc(trackNumber.hashCode, - present.hashCode))))))))); + blocked.hashCode))))))))); @override bool operator ==(dynamic other) => identical(this, other) || @@ -736,7 +692,7 @@ class MoorSong extends DataClass implements Insertable { other.duration == this.duration && other.albumArtPath == this.albumArtPath && other.trackNumber == this.trackNumber && - other.present == this.present); + other.blocked == this.blocked); } class SongsCompanion extends UpdateCompanion { @@ -748,7 +704,7 @@ class SongsCompanion extends UpdateCompanion { final Value duration; final Value albumArtPath; final Value trackNumber; - final Value present; + final Value blocked; const SongsCompanion({ this.title = const Value.absent(), this.albumTitle = const Value.absent(), @@ -758,7 +714,7 @@ class SongsCompanion extends UpdateCompanion { this.duration = const Value.absent(), this.albumArtPath = const Value.absent(), this.trackNumber = const Value.absent(), - this.present = const Value.absent(), + this.blocked = const Value.absent(), }); SongsCompanion.insert({ @required String title, @@ -769,7 +725,7 @@ class SongsCompanion extends UpdateCompanion { this.duration = const Value.absent(), this.albumArtPath = const Value.absent(), this.trackNumber = const Value.absent(), - this.present = const Value.absent(), + this.blocked = const Value.absent(), }) : title = Value(title), albumTitle = Value(albumTitle), albumId = Value(albumId), @@ -784,7 +740,7 @@ class SongsCompanion extends UpdateCompanion { Expression duration, Expression albumArtPath, Expression trackNumber, - Expression present, + Expression blocked, }) { return RawValuesInsertable({ if (title != null) 'title': title, @@ -795,7 +751,7 @@ class SongsCompanion extends UpdateCompanion { if (duration != null) 'duration': duration, if (albumArtPath != null) 'album_art_path': albumArtPath, if (trackNumber != null) 'track_number': trackNumber, - if (present != null) 'present': present, + if (blocked != null) 'blocked': blocked, }); } @@ -808,7 +764,7 @@ class SongsCompanion extends UpdateCompanion { Value duration, Value albumArtPath, Value trackNumber, - Value present}) { + Value blocked}) { return SongsCompanion( title: title ?? this.title, albumTitle: albumTitle ?? this.albumTitle, @@ -818,7 +774,7 @@ class SongsCompanion extends UpdateCompanion { duration: duration ?? this.duration, albumArtPath: albumArtPath ?? this.albumArtPath, trackNumber: trackNumber ?? this.trackNumber, - present: present ?? this.present, + blocked: blocked ?? this.blocked, ); } @@ -849,8 +805,8 @@ class SongsCompanion extends UpdateCompanion { if (trackNumber.present) { map['track_number'] = Variable(trackNumber.value); } - if (present.present) { - map['present'] = Variable(present.value); + if (blocked.present) { + map['blocked'] = Variable(blocked.value); } return map; } @@ -866,7 +822,7 @@ class SongsCompanion extends UpdateCompanion { ..write('duration: $duration, ') ..write('albumArtPath: $albumArtPath, ') ..write('trackNumber: $trackNumber, ') - ..write('present: $present') + ..write('blocked: $blocked') ..write(')')) .toString(); } @@ -976,13 +932,13 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> { ); } - final VerificationMeta _presentMeta = const VerificationMeta('present'); - GeneratedBoolColumn _present; + final VerificationMeta _blockedMeta = const VerificationMeta('blocked'); + GeneratedBoolColumn _blocked; @override - GeneratedBoolColumn get present => _present ??= _constructPresent(); - GeneratedBoolColumn _constructPresent() { - return GeneratedBoolColumn('present', $tableName, false, - defaultValue: const Constant(true)); + GeneratedBoolColumn get blocked => _blocked ??= _constructBlocked(); + GeneratedBoolColumn _constructBlocked() { + return GeneratedBoolColumn('blocked', $tableName, false, + defaultValue: const Constant(false)); } @override @@ -995,7 +951,7 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> { duration, albumArtPath, trackNumber, - present + blocked ]; @override $SongsTable get asDslTable => this; @@ -1056,9 +1012,9 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> { trackNumber.isAcceptableOrUnknown( data['track_number'], _trackNumberMeta)); } - if (data.containsKey('present')) { - context.handle(_presentMeta, - present.isAcceptableOrUnknown(data['present'], _presentMeta)); + if (data.containsKey('blocked')) { + context.handle(_blockedMeta, + blocked.isAcceptableOrUnknown(data['blocked'], _blockedMeta)); } return context; } diff --git a/lib/system/datasources/queue_manager.dart b/lib/system/datasources/queue_manager.dart index 6864cc6..05ffca6 100644 --- a/lib/system/datasources/queue_manager.dart +++ b/lib/system/datasources/queue_manager.dart @@ -24,9 +24,10 @@ class QueueManager { // TODO: test List generatePermutation( - ShuffleMode shuffleMode, int length, int startIndex) { + ShuffleMode shuffleMode, List mediaItems, int startIndex) { // permutation[i] = j; => song j is on the i-th position in the permutated list List permutation; + final int length = mediaItems.length; switch (shuffleMode) { case ShuffleMode.none: @@ -39,7 +40,7 @@ class QueueManager { permutation = [startIndex] + tmp; break; case ShuffleMode.plus: - break; + permutation = generatePlusPermutation(mediaItems, startIndex); } return permutation; @@ -57,4 +58,15 @@ class QueueManager { .map((MediaItem m) => AudioSource.uri(Uri.file(m.id))) .toList()); } + + List generatePlusPermutation( + List mediaItems, int startIndex) { + final List indices = []; + for (var i = 0; i < mediaItems.length; i++) { + if (!(mediaItems[i].extras['blocked'] as bool)) { + indices.add(i); + } + } + return [startIndex] + indices..removeAt(startIndex)..shuffle(); + } } diff --git a/lib/system/models/song_model.dart b/lib/system/models/song_model.dart index 8847395..88c9e6c 100644 --- a/lib/system/models/song_model.dart +++ b/lib/system/models/song_model.dart @@ -14,6 +14,7 @@ class SongModel extends Song { @required String artist, @required String path, @required int duration, + @required bool blocked, int trackNumber, String albumArtPath}) : super( @@ -22,6 +23,7 @@ class SongModel extends Song { artist: artist, path: path, duration: duration, + blocked: blocked, trackNumber: trackNumber, albumArtPath: albumArtPath, ); @@ -33,8 +35,9 @@ class SongModel extends Song { albumId: moorSong.albumId, path: moorSong.path, duration: moorSong.duration, - albumArtPath: moorSong.albumArtPath, + blocked: moorSong.blocked, trackNumber: moorSong.trackNumber, + albumArtPath: moorSong.albumArtPath, ); factory SongModel.fromSongInfo(SongInfo songInfo) { @@ -47,6 +50,7 @@ class SongModel extends Song { albumId: int.parse(songInfo.albumId), path: songInfo.filePath, duration: duration == null ? null : int.parse(duration), + blocked: false, albumArtPath: songInfo.albumArtwork, trackNumber: _parseTrackNumber(songInfo.track), ); @@ -74,6 +78,7 @@ class SongModel extends Song { artist: mediaItem.artist, path: mediaItem.id, duration: mediaItem.duration.inMilliseconds, + blocked: mediaItem.extras['blocked'] == 'true', albumArtPath: artUri, trackNumber: trackNumber, ); @@ -92,6 +97,7 @@ class SongModel extends Song { String artist, String path, int duration, + bool blocked, int trackNumber, String albumArtPath, int albumId, @@ -102,6 +108,7 @@ class SongModel extends Song { duration: duration ?? this.duration, path: path ?? this.path, title: title ?? this.title, + blocked: blocked ?? this.blocked, trackNumber: trackNumber ?? this.trackNumber, albumArtPath: albumArtPath ?? this.albumArtPath, albumId: albumId ?? this.albumId, @@ -114,6 +121,7 @@ class SongModel extends Song { title: Value(title), path: Value(path), duration: Value(duration), + blocked: Value(blocked), albumArtPath: Value(albumArtPath), trackNumber: Value(trackNumber), ); @@ -127,6 +135,7 @@ class SongModel extends Song { artUri: 'file://$albumArtPath', extras: { 'albumId': albumId, + 'blocked': blocked.toString(), 'trackNumber': trackNumber, }); diff --git a/lib/system/repositories/music_data_repository_impl.dart b/lib/system/repositories/music_data_repository_impl.dart index 7e4cb3b..f6ada0c 100644 --- a/lib/system/repositories/music_data_repository_impl.dart +++ b/lib/system/repositories/music_data_repository_impl.dart @@ -74,4 +74,10 @@ class MusicDataRepositoryImpl implements MusicDataRepository { await musicDataSource.insertSong(songToInsert); } } + + @override + Future setSongBlocked(Song song, bool blocked) { + // TODO: implement setSongBlocked + throw UnimplementedError(); + } } diff --git a/pubspec.lock b/pubspec.lock index 92874dd..ee13412 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,14 +42,14 @@ packages: name: audio_service url: "https://pub.dartlang.org" source: hosted - version: "0.14.0" + version: "0.15.0" audio_session: dependency: "direct main" description: name: audio_session url: "https://pub.dartlang.org" source: hosted - version: "0.0.3" + version: "0.0.7" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c37891f..fbdc185 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter_audio_query: ^0.3.5 - audio_service: ^0.14.0 + audio_service: ^0.15.0 audio_session: ^0.0.3 just_audio: ^0.4.0 diff --git a/test/system/datasources/moor_music_data_source_test.dart b/test/system/datasources/moor_music_data_source_test.dart index 8da35b4..6be2500 100644 --- a/test/system/datasources/moor_music_data_source_test.dart +++ b/test/system/datasources/moor_music_data_source_test.dart @@ -29,6 +29,7 @@ void main() { artist: ARTIST_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, trackNumber: TRACKNUMBER_3, albumArtPath: ALBUM_ART_PATH_3, ); diff --git a/test/system/models/album_model_test.dart b/test/system/models/album_model_test.dart index 8d9304f..d0830a2 100644 --- a/test/system/models/album_model_test.dart +++ b/test/system/models/album_model_test.dart @@ -66,7 +66,6 @@ void main() { title: ALBUM_TITLE_1, albumArtPath: ALBUM_ART_PATH_1, year: YEAR_1, - present: PRESENT_1, ); const expected = AlbumModel( @@ -92,7 +91,6 @@ void main() { artist: ARTIST_1, title: ALBUM_TITLE_1, year: YEAR_1, - present: PRESENT_1, ); const expected = AlbumModel( @@ -117,7 +115,6 @@ void main() { artist: ARTIST_1, title: ALBUM_TITLE_1, albumArtPath: ALBUM_ART_PATH_1, - present: PRESENT_1, ); const expected = AlbumModel( diff --git a/test/system/models/song_model_test.dart b/test/system/models/song_model_test.dart index cbcae08..d5d5a13 100644 --- a/test/system/models/song_model_test.dart +++ b/test/system/models/song_model_test.dart @@ -19,6 +19,7 @@ void main() { artist: ARTIST_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, trackNumber: TRACKNUMBER_3, albumArtPath: ALBUM_ART_PATH_3, ); @@ -43,6 +44,7 @@ void main() { title: Value(SONG_TITLE_3), path: Value(PATH_3), duration: Value(DURATION_3), + blocked: Value(BLOCKED_3), albumArtPath: Value(ALBUM_ART_PATH_3), trackNumber: Value(TRACKNUMBER_3), ); @@ -54,6 +56,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -86,6 +89,7 @@ void main() { extras: { 'albumId': ALBUM_ID_3, 'trackNumber': TRACKNUMBER_3, + 'blocked': BLOCKED_3 }); const songModel = SongModel( @@ -95,6 +99,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -126,7 +131,7 @@ void main() { duration: DURATION_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, - present: PRESENT_3, + blocked: BLOCKED_3, ); const expected = SongModel( @@ -136,6 +141,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -163,7 +169,7 @@ void main() { }); test( - 'should create SongModel from AlbumInfo', + 'should create SongModel from SongInfo', () async { // arrange const expected = SongModel( @@ -173,6 +179,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: false, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -199,6 +206,7 @@ void main() { extras: { 'albumId': ALBUM_ID_3, 'trackNumber': TRACKNUMBER_3, + 'blocked': BLOCKED_3, }, ); @@ -209,6 +217,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -244,6 +253,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -260,6 +270,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); @@ -281,6 +292,7 @@ void main() { title: SONG_TITLE_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, albumArtPath: ALBUM_ART_PATH_3, trackNumber: TRACKNUMBER_3, ); diff --git a/test/system/repositories/audio_repository_impl_test.dart b/test/system/repositories/audio_repository_impl_test.dart index cb045db..635f446 100644 --- a/test/system/repositories/audio_repository_impl_test.dart +++ b/test/system/repositories/audio_repository_impl_test.dart @@ -41,6 +41,7 @@ List setupSongList() => [ artist: ARTIST_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, trackNumber: TRACKNUMBER_3, albumArtPath: ALBUM_ART_PATH_3, ), @@ -51,6 +52,7 @@ List setupSongList() => [ artist: ARTIST_4, path: PATH_4, duration: DURATION_4, + blocked: BLOCKED_4, trackNumber: TRACKNUMBER_4, albumArtPath: ALBUM_ART_PATH_4, ), diff --git a/test/system/repositories/music_data_repository_impl_test.dart b/test/system/repositories/music_data_repository_impl_test.dart index acb3b30..8b283bd 100644 --- a/test/system/repositories/music_data_repository_impl_test.dart +++ b/test/system/repositories/music_data_repository_impl_test.dart @@ -127,6 +127,7 @@ List setupSongList() => [ artist: ARTIST_3, path: PATH_3, duration: DURATION_3, + blocked: BLOCKED_3, trackNumber: TRACKNUMBER_3, albumArtPath: ALBUM_ART_PATH_3, ), @@ -137,6 +138,7 @@ List setupSongList() => [ artist: ARTIST_4, path: PATH_4, duration: DURATION_4, + blocked: BLOCKED_4, trackNumber: TRACKNUMBER_4, albumArtPath: ALBUM_ART_PATH_4, ), diff --git a/test/test_constants.dart b/test/test_constants.dart index 58fc3b4..04ae3ad 100644 --- a/test/test_constants.dart +++ b/test/test_constants.dart @@ -26,7 +26,7 @@ const String PATH_3 = '/music/parkwaydrive/bottom_feeder.mp3'; const int DURATION_3 = 180000; const String ALBUM_ART_PATH_3 = '/music/parkwaydrive/ire.jpg'; const int TRACKNUMBER_3 = 7; -const bool PRESENT_3 = true; +const bool BLOCKED_3 = false; const String SONG_TITLE_4 = 'Black Flame'; const String ALBUM_TITLE_4 = 'Black Flame'; @@ -36,4 +36,4 @@ const String PATH_4 = '/music/burytomorrow/blackflame.mp3'; const int DURATION_4 = 240000; const String ALBUM_ART_PATH_4 = '/music/parkwaydrive/blackflame.jpg'; const int TRACKNUMBER_4 = 3; -const bool PRESENT_4 = false; \ No newline at end of file +const bool BLOCKED_4 = false; \ No newline at end of file