| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  | import 'dart:convert'; | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | import 'dart:isolate'; | 
					
						
							|  |  |  | import 'dart:ui'; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import 'package:audio_service/audio_service.dart'; | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  | import 'package:moor/isolate.dart'; | 
					
						
							|  |  |  | import 'package:musicus_database/musicus_database.dart'; | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  | import 'package:musicus_common/musicus_common.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  | import 'package:musicus_player/musicus_player.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | const _portName = 'playbackService'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | /// Entrypoint for the playback service.
 | 
					
						
							|  |  |  | void _playbackServiceEntrypoint() { | 
					
						
							|  |  |  |   AudioServiceBackground.run(() => _PlaybackService()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  | class Playback extends MusicusPlayback { | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   StreamSubscription _playbackServiceStateSubscription; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// Start playback service.
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   Future<void> _start() async { | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     if (!AudioService.running) { | 
					
						
							|  |  |  |       await AudioService.start( | 
					
						
							|  |  |  |         backgroundTaskEntrypoint: _playbackServiceEntrypoint, | 
					
						
							|  |  |  |         androidNotificationChannelName: 'Musicus playback', | 
					
						
							|  |  |  |         androidNotificationChannelDescription: | 
					
						
							|  |  |  |             'Keeps Musicus playing in the background', | 
					
						
							|  |  |  |         androidNotificationIcon: 'drawable/ic_notification', | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |       active.add(true); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Update [position] and [normalizedPosition].
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   ///
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Requires [duration] to be up to date
 | 
					
						
							|  |  |  |   void _updatePosition(int positionMs) { | 
					
						
							|  |  |  |     position.add(Duration(milliseconds: positionMs)); | 
					
						
							|  |  |  |     normalizedPosition.add(positionMs / duration.value.inMilliseconds); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Update [position], [duration] and [normalizedPosition].
 | 
					
						
							|  |  |  |   void _updateDuration(int positionMs, int durationMs) { | 
					
						
							|  |  |  |     position.add(Duration(milliseconds: positionMs)); | 
					
						
							|  |  |  |     duration.add(Duration(milliseconds: durationMs)); | 
					
						
							|  |  |  |     normalizedPosition.add(positionMs / durationMs); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Update [currentIndex] and [currentTrack].
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   ///
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Requires [playlist] to be up to date.
 | 
					
						
							|  |  |  |   void _updateCurrentTrack(int index) { | 
					
						
							|  |  |  |     currentIndex.add(index); | 
					
						
							|  |  |  |     currentTrack.add(playlist.value[index]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Future<void> setup() async { | 
					
						
							| 
									
										
										
										
											2020-05-03 22:45:28 +02:00
										 |  |  |     if (_playbackServiceStateSubscription != null) { | 
					
						
							|  |  |  |       _playbackServiceStateSubscription.cancel(); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-03 22:45:28 +02:00
										 |  |  |     // We will receive updated state information from the playback service,
 | 
					
						
							|  |  |  |     // which runs in its own isolate, through this port.
 | 
					
						
							|  |  |  |     final receivePort = ReceivePort(); | 
					
						
							|  |  |  |     receivePort.asBroadcastStream( | 
					
						
							|  |  |  |       onListen: (subscription) { | 
					
						
							|  |  |  |         _playbackServiceStateSubscription = subscription; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ).listen((msg) { | 
					
						
							|  |  |  |       // If state is null, the background audio service has stopped.
 | 
					
						
							|  |  |  |       if (msg == null) { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |         dispose(); | 
					
						
							| 
									
										
										
										
											2020-05-03 22:45:28 +02:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |         if (!active.value) { | 
					
						
							|  |  |  |           active.add(true); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-03 22:45:28 +02:00
										 |  |  |         if (msg is _StatusMessage) { | 
					
						
							|  |  |  |           playing.add(msg.playing); | 
					
						
							|  |  |  |         } else if (msg is _PositionMessage) { | 
					
						
							|  |  |  |           _updatePosition(msg.positionMs); | 
					
						
							|  |  |  |         } else if (msg is _TrackMessage) { | 
					
						
							|  |  |  |           _updateCurrentTrack(msg.currentTrack); | 
					
						
							|  |  |  |           _updateDuration(msg.positionMs, msg.durationMs); | 
					
						
							|  |  |  |         } else if (msg is _PlaylistMessage) { | 
					
						
							|  |  |  |           playlist.add(msg.playlist); | 
					
						
							|  |  |  |           _updateCurrentTrack(msg.currentTrack); | 
					
						
							|  |  |  |           _updateDuration(msg.positionMs, msg.durationMs); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-03 22:45:28 +02:00
										 |  |  |     IsolateNameServer.removePortNameMapping(_portName); | 
					
						
							|  |  |  |     IsolateNameServer.registerPortWithName(receivePort.sendPort, _portName); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     if (AudioService.running) { | 
					
						
							|  |  |  |       active.add(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Instruct the background service to send its current state. This will
 | 
					
						
							|  |  |  |       // by handled in the listeners, that were already set in the constructor.
 | 
					
						
							|  |  |  |       AudioService.customAction('sendState'); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   Future<void> addTracks(List<InternalTrack> tracks) async { | 
					
						
							|  |  |  |     if (!AudioService.running) { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |       await _start(); | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await AudioService.customAction('addTracks', jsonEncode(tracks)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-26 18:54:49 +02:00
										 |  |  |   Future<void> removeTrack(int index) async { | 
					
						
							|  |  |  |     if (AudioService.running) { | 
					
						
							|  |  |  |       await AudioService.customAction('removeTrack', index); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Future<void> playPause() async { | 
					
						
							|  |  |  |     if (active.value) { | 
					
						
							|  |  |  |       if (playing.value) { | 
					
						
							|  |  |  |         await AudioService.pause(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         await AudioService.play(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   Future<void> seekTo(double pos) async { | 
					
						
							|  |  |  |     if (active.value && pos >= 0.0 && pos <= 1.0) { | 
					
						
							|  |  |  |       final durationMs = duration.value.inMilliseconds; | 
					
						
							|  |  |  |       await AudioService.seekTo((pos * durationMs).floor()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Future<void> skipToPrevious() async { | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     if (AudioService.running) { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |       await AudioService.skipToPrevious(); | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Future<void> skipToNext() async { | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     if (AudioService.running) { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |       await AudioService.skipToNext(); | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |   Future<void> skipTo(int index) async { | 
					
						
							|  |  |  |     if (AudioService.running) { | 
					
						
							|  |  |  |       await AudioService.customAction('skipTo', index); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   void dispose() { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |     super.dispose(); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     _playbackServiceStateSubscription.cancel(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | /// A message from the playback service to the UI.
 | 
					
						
							|  |  |  | abstract class _Message {} | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | /// Playback status update.
 | 
					
						
							|  |  |  | class _StatusMessage extends _Message { | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   /// Whether the player is playing (or paused).
 | 
					
						
							|  |  |  |   final bool playing; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   /// Playback position in milliseconds.
 | 
					
						
							|  |  |  |   final int positionMs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   _StatusMessage({ | 
					
						
							|  |  |  |     this.playing, | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |     this.positionMs, | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// The playback position has changed.
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// This could be due to seeking or because time progressed.
 | 
					
						
							|  |  |  | class _PositionMessage extends _Message { | 
					
						
							|  |  |  |   /// Playback position in milliseconds.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   final int positionMs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   _PositionMessage({ | 
					
						
							|  |  |  |     this.positionMs, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// The current track has changed.
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// This also notifies about the playback position, as the old position could be
 | 
					
						
							|  |  |  | /// behind the new duration.
 | 
					
						
							|  |  |  | class _TrackMessage extends _Message { | 
					
						
							|  |  |  |   /// Index of the new track within the playlist.
 | 
					
						
							|  |  |  |   final int currentTrack; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Duration of the new track in milliseconds.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   final int durationMs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Playback position in milliseconds.
 | 
					
						
							|  |  |  |   final int positionMs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _TrackMessage({ | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     this.currentTrack, | 
					
						
							|  |  |  |     this.durationMs, | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     this.positionMs, | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | /// The playlist was changed.
 | 
					
						
							|  |  |  | ///
 | 
					
						
							|  |  |  | /// This also notifies about the current track, as the old index could be out of
 | 
					
						
							|  |  |  | /// range in the new playlist.
 | 
					
						
							|  |  |  | class _PlaylistMessage extends _Message { | 
					
						
							|  |  |  |   /// The new playlist.
 | 
					
						
							|  |  |  |   final List<InternalTrack> playlist; | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// The current track.
 | 
					
						
							|  |  |  |   final int currentTrack; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Duration of the current track in milliseconds.
 | 
					
						
							|  |  |  |   final int durationMs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Playback position in milliseconds.
 | 
					
						
							|  |  |  |   final int positionMs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _PlaylistMessage({ | 
					
						
							|  |  |  |     this.playlist, | 
					
						
							|  |  |  |     this.currentTrack, | 
					
						
							|  |  |  |     this.durationMs, | 
					
						
							|  |  |  |     this.positionMs, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | class _PlaybackService extends BackgroundAudioTask { | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   /// The interval between playback position updates in milliseconds.
 | 
					
						
							|  |  |  |   static const positionUpdateInterval = 250; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   static const playControl = MediaControl( | 
					
						
							|  |  |  |     androidIcon: 'drawable/ic_play', | 
					
						
							|  |  |  |     label: 'Play', | 
					
						
							|  |  |  |     action: MediaAction.play, | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static const pauseControl = MediaControl( | 
					
						
							|  |  |  |     androidIcon: 'drawable/ic_pause', | 
					
						
							|  |  |  |     label: 'Pause', | 
					
						
							|  |  |  |     action: MediaAction.pause, | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static const stopControl = MediaControl( | 
					
						
							|  |  |  |     androidIcon: 'drawable/ic_stop', | 
					
						
							|  |  |  |     label: 'Stop', | 
					
						
							|  |  |  |     action: MediaAction.stop, | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   final _completer = Completer(); | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  |   final _loading = Completer(); | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   final List<InternalTrack> _playlist = []; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  |   Database db; | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   MusicusPlayer _player; | 
					
						
							|  |  |  |   int _currentTrack = 0; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   bool _playing = false; | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   int _durationMs = 1000; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   _PlaybackService() { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     _player = MusicusPlayer(onComplete: () async { | 
					
						
							|  |  |  |       if (_currentTrack < _playlist.length - 1) { | 
					
						
							|  |  |  |         await _setCurrentTrack(_currentTrack + 1); | 
					
						
							|  |  |  |         _sendTrack(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         _playing = false; | 
					
						
							|  |  |  |         _sendStatus(); | 
					
						
							|  |  |  |         _setState(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _load(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Initialize database.
 | 
					
						
							|  |  |  |   Future<void> _load() async { | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |     final moorPort = IsolateNameServer.lookupPortByName('moor'); | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  |     final moorIsolate = MoorIsolate.fromConnectPort(moorPort); | 
					
						
							|  |  |  |     db = Database.connect(await moorIsolate.connect()); | 
					
						
							|  |  |  |     _loading.complete(); | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Update the audio service status for the system.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   Future<void> _setState() async { | 
					
						
							|  |  |  |     final positionMs = await _player.getPosition() ?? 0; | 
					
						
							|  |  |  |     final updateTime = DateTime.now().millisecondsSinceEpoch; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     AudioServiceBackground.setState( | 
					
						
							|  |  |  |       controls: | 
					
						
							|  |  |  |           _playing ? [pauseControl, stopControl] : [playControl, stopControl], | 
					
						
							|  |  |  |       basicState: | 
					
						
							|  |  |  |           _playing ? BasicPlaybackState.playing : BasicPlaybackState.paused, | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |       position: positionMs, | 
					
						
							|  |  |  |       updateTime: updateTime, | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  |     if (_playlist.isNotEmpty) { | 
					
						
							|  |  |  |       await _loading.future; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       final track = _playlist[_currentTrack]; | 
					
						
							|  |  |  |       final recordingInfo = await db.getRecording(track.track.recordingId); | 
					
						
							|  |  |  |       final workInfo = await db.getWork(recordingInfo.recording.work); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       final composers = workInfo.composers | 
					
						
							|  |  |  |           .map((p) => '${p.firstName} ${p.lastName}') | 
					
						
							|  |  |  |           .join(', '); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       final title = workInfo.work.title; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       AudioServiceBackground.setMediaItem(MediaItem( | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |         id: track.identifier, | 
					
						
							| 
									
										
										
										
											2020-05-04 09:23:49 +02:00
										 |  |  |         album: composers, | 
					
						
							|  |  |  |         title: title, | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Send a message to the UI.
 | 
					
						
							|  |  |  |   void _sendMsg(_Message msg) { | 
					
						
							|  |  |  |     final sendPort = IsolateNameServer.lookupPortByName(_portName); | 
					
						
							|  |  |  |     sendPort?.send(msg); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Notify the UI about the current playback status.
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   Future<void> _sendStatus() async { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     _sendMsg(_StatusMessage( | 
					
						
							|  |  |  |       playing: _playing, | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |       positionMs: await _player.getPosition(), | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Notify the UI about the current playback position.
 | 
					
						
							|  |  |  |   Future<void> _sendPosition() async { | 
					
						
							|  |  |  |     _sendMsg(_PositionMessage( | 
					
						
							|  |  |  |       positionMs: await _player.getPosition(), | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Notify the UI about the current track.
 | 
					
						
							|  |  |  |   Future<void> _sendTrack() async { | 
					
						
							|  |  |  |     _sendMsg(_TrackMessage( | 
					
						
							|  |  |  |       currentTrack: _currentTrack, | 
					
						
							|  |  |  |       durationMs: _durationMs, | 
					
						
							|  |  |  |       positionMs: await _player.getPosition(), | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Notify the UI about the current playlist.
 | 
					
						
							|  |  |  |   Future<void> _sendPlaylist() async { | 
					
						
							|  |  |  |     _sendMsg(_PlaylistMessage( | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |       playlist: _playlist, | 
					
						
							|  |  |  |       currentTrack: _currentTrack, | 
					
						
							|  |  |  |       durationMs: _durationMs, | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |       positionMs: await _player.getPosition(), | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   /// Notify the UI of the new playback position periodically.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |   Future<void> _updatePosition() async { | 
					
						
							|  |  |  |     while (_playing) { | 
					
						
							| 
									
										
										
										
											2020-05-13 18:22:39 +02:00
										 |  |  |       _sendPosition(); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |       await Future.delayed( | 
					
						
							|  |  |  |           const Duration(milliseconds: positionUpdateInterval)); | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   /// Set the current track, update the player and notify the system.
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   Future<void> _setCurrentTrack(int index) async { | 
					
						
							|  |  |  |     _currentTrack = index; | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |     _durationMs = await _player.setUri(_playlist[_currentTrack].identifier); | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     _setState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Add [tracks] to the playlist.
 | 
					
						
							|  |  |  |   Future<void> _addTracks(List<InternalTrack> tracks) async { | 
					
						
							|  |  |  |     final play = _playlist.isEmpty; | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     _playlist.addAll(tracks); | 
					
						
							|  |  |  |     if (play) { | 
					
						
							|  |  |  |       await _setCurrentTrack(0); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _sendPlaylist(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 18:54:49 +02:00
										 |  |  |   /// Remove the track at [index] from the playlist.
 | 
					
						
							|  |  |  |   ///
 | 
					
						
							|  |  |  |   /// If it was the current track, the next track will be played.
 | 
					
						
							|  |  |  |   Future<void> _removeTrack(int index) async { | 
					
						
							|  |  |  |     if (index >= 0 && index < _playlist.length) { | 
					
						
							|  |  |  |       _playlist.removeAt(index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (_playlist.isEmpty) { | 
					
						
							|  |  |  |         onStop(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         if (_currentTrack == index) { | 
					
						
							|  |  |  |           await _setCurrentTrack(index); | 
					
						
							|  |  |  |         } else if (_currentTrack > index) { | 
					
						
							|  |  |  |           _currentTrack--; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         _sendPlaylist(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |   /// Jump to the beginning of the track with the index [index].
 | 
					
						
							|  |  |  |   Future<void> _skipTo(int index) async { | 
					
						
							|  |  |  |     if (index >= 0 && index < _playlist.length) { | 
					
						
							|  |  |  |       await _setCurrentTrack(index); | 
					
						
							|  |  |  |       _sendTrack(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Future<void> onStart() async { | 
					
						
							|  |  |  |     _setState(); | 
					
						
							|  |  |  |     await _completer.future; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-05-04 21:49:44 +02:00
										 |  |  |   Future<void> onCustomAction(String name, dynamic arguments) async { | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     super.onCustomAction(name, arguments); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // addTracks expects a List<Map<String, dynamic>> as its argument.
 | 
					
						
							| 
									
										
										
										
											2020-04-26 18:54:49 +02:00
										 |  |  |     // skipTo and removeTrack expect an integer as their argument.
 | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     if (name == 'addTracks') { | 
					
						
							|  |  |  |       final tracksJson = jsonDecode(arguments); | 
					
						
							|  |  |  |       final List<InternalTrack> tracks = List.castFrom( | 
					
						
							|  |  |  |           tracksJson.map((j) => InternalTrack.fromJson(j)).toList()); | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       _addTracks(tracks); | 
					
						
							| 
									
										
										
										
											2020-04-26 18:54:49 +02:00
										 |  |  |     } else if (name == 'removeTrack') { | 
					
						
							|  |  |  |       final index = arguments as int; | 
					
						
							|  |  |  |       _removeTrack(index); | 
					
						
							|  |  |  |     } else if (name == 'skipTo') { | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |       final index = arguments as int; | 
					
						
							| 
									
										
										
										
											2020-04-24 21:49:14 +02:00
										 |  |  |       _skipTo(index); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     } else if (name == 'sendState') { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |       _sendPlaylist(); | 
					
						
							|  |  |  |       _sendStatus(); | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onPlay() { | 
					
						
							|  |  |  |     super.onPlay(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     _player.play(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     _playing = true; | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _sendStatus(); | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     _updatePosition(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     _setState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void onPause() { | 
					
						
							|  |  |  |     super.onPause(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     _player.pause(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     _playing = false; | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _sendStatus(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     _setState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   Future<void> onSeekTo(int position) async { | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     super.onSeekTo(position); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |     await _player.seekTo(position); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _sendPosition(); | 
					
						
							|  |  |  |     _setState(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   Future<void> onSkipToNext() async { | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     super.onSkipToNext(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_playlist.length > 1 && _currentTrack < _playlist.length - 1) { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |       await _setCurrentTrack(_currentTrack + 1); | 
					
						
							|  |  |  |       _sendTrack(); | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |   Future<void> onSkipToPrevious() async { | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     super.onSkipToPrevious(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If more than five seconds of the current track have been played, go back
 | 
					
						
							|  |  |  |     // to its beginning, else, switch to the previous track.
 | 
					
						
							|  |  |  |     if (await _player.getPosition() > 5000) { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |       await _setCurrentTrack(_currentTrack); | 
					
						
							|  |  |  |       _sendTrack(); | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     } else if (_playlist.length > 1 && _currentTrack > 0) { | 
					
						
							| 
									
										
										
										
											2020-04-24 19:50:03 +02:00
										 |  |  |       await _setCurrentTrack(_currentTrack - 1); | 
					
						
							|  |  |  |       _sendTrack(); | 
					
						
							| 
									
										
										
										
											2020-04-22 10:01:50 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onStop() { | 
					
						
							| 
									
										
										
										
											2020-04-21 17:37:01 +02:00
										 |  |  |     _player.stop(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     AudioServiceBackground.setState( | 
					
						
							|  |  |  |       controls: [], | 
					
						
							|  |  |  |       basicState: BasicPlaybackState.stopped, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 19:50:18 +02:00
										 |  |  |     _sendMsg(null); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     // This will end onStart.
 | 
					
						
							|  |  |  |     _completer.complete(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |