diff --git a/lib/state/audio_state.dart b/lib/state/audio_state.dart index a33967e..021a789 100644 --- a/lib/state/audio_state.dart +++ b/lib/state/audio_state.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:math' as math; import 'package:audio_service/audio_service.dart'; import 'package:audio_session/audio_session.dart'; import 'package:dio/dio.dart'; +import 'package:rxdart/rxdart.dart'; import 'package:flutter/foundation.dart'; import 'package:just_audio/just_audio.dart'; @@ -51,10 +51,6 @@ MediaControl rewindControl = MediaControl( action: MediaAction.rewind, ); -void _audioPlayerTaskEntrypoint() async { - AudioServiceBackground.run(() => AudioPlayerTask()); -} - /// Sleep timer mode. enum SleepTimerMode { endOfEpisode, timer, undefined } enum PlayerHeight { short, mid, tall } @@ -93,7 +89,7 @@ class AudioPlayerNotifier extends ChangeNotifier { Playlist get _queue => _playlists.first; /// Player state. - AudioProcessingState _audioState = AudioProcessingState.none; + AudioProcessingState _audioState = AudioProcessingState.loading; /// Player playing. bool _playing = false; @@ -178,17 +174,23 @@ class AudioPlayerNotifier extends ChangeNotifier { // Tmep episode list, playing from search result List _playFromSearchList = []; - @override - void addListener(VoidCallback listener) { - super.addListener(listener); - _initAudioData(); - AudioService.connect(); - } + AudioHandler _audioHandler; + AudioSession _session; @override - void dispose() async { - await AudioService.disconnect(); - super.dispose(); + void addListener(VoidCallback listener) async { + super.addListener(listener); + _initAudioData(); + _audioHandler = await AudioService.init( + builder: () => CustomAudioHandler(), + config: AudioServiceConfig( + androidNotificationChannelName: 'Tsacdop', + androidNotificationIcon: 'drawable/ic_notification', + androidEnableQueue: true, + androidStopForegroundOnPause: true, + fastForwardInterval: Duration(seconds: _fastForwardSeconds), + rewindInterval: Duration(seconds: _rewindSeconds)), + ); } /// Audio playing state. @@ -326,7 +328,7 @@ class AudioPlayerNotifier extends ChangeNotifier { if (_episode == null || !_playlist.episodes.contains(_episode)) { _episode = _playlist.isNotEmpty ? _playlist.episodes.first : null; } - _audioState = AudioProcessingState.none; + _audioState = AudioProcessingState.loading; _backgroundAudioDuration = 0; _backgroundAudioPosition = 0; _seekSliderValue = 0; @@ -347,15 +349,17 @@ class AudioPlayerNotifier extends ChangeNotifier { notifyListeners(); if (playlist.isNotEmpty) { if (playerRunning) { - AudioService.customAction('setIsQueue', playlist.name == 'Queue'); - AudioService.customAction('changeQueue', - [for (var e in p.episodes) jsonEncode(e.toMediaItem().toJson())]); + _audioHandler + .customAction('setIsQueue', {'isQueue': playlist.name == 'Queue'}); + _audioHandler.customAction('changeQueue', { + 'queue': [for (var e in p.episodes) e.toMediaItem()] + }); } else { _backgroundAudioDuration = 0; _backgroundAudioPosition = 0; _seekSliderValue = 0; _episode = playlist.episodes.first; - _audioState = AudioProcessingState.none; + _audioState = AudioProcessingState.loading; _playerRunning = true; notifyListeners(); _startAudioService(_playlist, position: 0, index: 0); @@ -384,15 +388,16 @@ class AudioPlayerNotifier extends ChangeNotifier { _queue.addToPlayListAt(episodeNew, 0); await updatePlaylist(_queue, updateEpisodes: !fromSearch); if (!_playlist.isQueue) { - AudioService.customAction('setIsQueue', true); - AudioService.customAction('changeQueue', [ - for (var e in _queue.episodes) jsonEncode(e.toMediaItem().toJson()) - ]); + _audioHandler.customAction('setIsQueue', {'isQueue': true}); + _audioHandler.customAction('changeQueue', { + 'queue': [for (var e in _queue.episodes) e.toMediaItem()] + }); _playlist = _queue; } - await AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0); + await _audioHandler.customAction('addQueueItemAt', + {'mediaItem': episodeNew.toMediaItem(), 'index': 0}); if (startPosition > 0) { - await AudioService.seekTo(Duration(milliseconds: startPosition)); + await _audioHandler.seek(Duration(milliseconds: startPosition)); } _remoteErrorMessage = null; notifyListeners(); @@ -417,7 +422,7 @@ class AudioPlayerNotifier extends ChangeNotifier { Future loadEpisodeFromPlaylist(EpisodeBrief episode) async { if (_playlist.episodes.contains(episode)) { var index = _playlist.episodes.indexOf(episode); - await AudioService.customAction('changeIndex', index); + await _audioHandler.customAction('changeIndex', {'index': index}); } } @@ -427,11 +432,6 @@ class AudioPlayerNotifier extends ChangeNotifier { _sleepTimerMode = SleepTimerMode.undefined; _switchValue = 0; - /// Connect to audio service. - if (!AudioService.connected) { - await AudioService.connect(); - } - /// Get fastword and rewind seconds. _fastForwardSeconds = await _fastForwardSecondsStorage.getInt(defaultValue: 30); @@ -442,25 +442,15 @@ class AudioPlayerNotifier extends ChangeNotifier { await _markListenedAfterSkipStorage.getBool(defaultValue: false); /// Start audio service. - await AudioService.start( - backgroundTaskEntrypoint: _audioPlayerTaskEntrypoint, - params: {'index': index, 'isQueue': playlist.name == 'Queue'}, - androidNotificationChannelName: 'Tsacdop', - androidNotificationColor: 0xFF4d91be, - androidNotificationIcon: 'drawable/ic_notification', - androidEnableQueue: true, - androidStopForegroundOnPause: true, - fastForwardInterval: Duration(seconds: _fastForwardSeconds), - rewindInterval: Duration(seconds: _rewindSeconds)); //Check autoplay setting, if true only add one episode, else add playlist. await _getAutoPlay(); if (_autoPlay) { for (var episode in playlist.episodes) { - await AudioService.addQueueItem(episode.toMediaItem()); + await _audioHandler.addQueueItem(episode.toMediaItem()); } } else { - await AudioService.addQueueItem(playlist.episodes[index].toMediaItem()); + await _audioHandler.addQueueItem(playlist.episodes[index].toMediaItem()); } //Check auto sleep timer setting await _getAutoSleepTimer(); @@ -483,23 +473,22 @@ class AudioPlayerNotifier extends ChangeNotifier { /// Set player speed. if (_currentSpeed != 1.0) { - await AudioService.customAction('setSpeed', _currentSpeed); + await _audioHandler.customAction('setSpeed', {'speed': _currentSpeed}); } /// Set slipsilence. if (_skipSilence) { - await AudioService.customAction('setSkipSilence', skipSilence); + // await _audioHandler.customAction('setSkipSilence', {'': skipSilence}); } /// Set boostValome. - if (_boostVolume) { - await AudioService.customAction( - 'setBoostVolume', [_boostVolume, _volumeGain]); - } + // if (_boostVolume) { + // await _audioHandler.customAction( + // 'setBoostVolume', [_boostVolume, _volumeGain]); + // } - await AudioService.play(); - - AudioService.currentMediaItemStream + await _audioHandler.play(); + _audioHandler.mediaItem .where((event) => event != null) .listen((item) async { var episode = await _dbHelper.getRssItemWithMediaId(item.id); @@ -522,7 +511,7 @@ class AudioPlayerNotifier extends ChangeNotifier { AudioService.skipToNext(); } }); - AudioService.playbackStateStream + _audioHandler.playbackState .distinct() .where((event) => event != null) .listen((event) async { @@ -530,9 +519,9 @@ class AudioPlayerNotifier extends ChangeNotifier { _audioState = event.processingState; _playing = event?.playing; _currentSpeed = event.speed; - _currentPosition = event.currentPosition.inMilliseconds ?? 0; + _currentPosition = event.position.inMilliseconds ?? 0; - if (_audioState == AudioProcessingState.stopped) { + if (_audioState == AudioProcessingState.completed) { if (_switchValue > 0) _switchValue = 0; } @@ -548,7 +537,7 @@ class AudioPlayerNotifier extends ChangeNotifier { notifyListeners(); }); - AudioService.customEventStream.distinct().listen((event) async { + _audioHandler.customEvent.distinct().listen((event) async { if (event is String) { if (_playlist.isQueue && _queue.isNotEmpty && @@ -615,7 +604,7 @@ class AudioPlayerNotifier extends ChangeNotifier { } notifyListeners(); } - if (_audioState == AudioProcessingState.stopped) { + if (_audioState == AudioProcessingState.completed) { timer.cancel(); } }); @@ -629,7 +618,7 @@ class AudioPlayerNotifier extends ChangeNotifier { } if (!_queue.episodes.contains(episodeNew)) { if (playerRunning && _playlist.isQueue) { - await AudioService.addQueueItem(episodeNew.toMediaItem()); + await _audioHandler.addQueueItem(episodeNew.toMediaItem()); } if (_playlist.isQueue && _queue.isEmpty) _episode = episodeNew; _queue.addToPlayList(episodeNew); @@ -643,7 +632,8 @@ class AudioPlayerNotifier extends ChangeNotifier { await _dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl); } if (_playerRunning && _playlist.isQueue) { - await AudioService.addQueueItemAt(episodeNew.toMediaItem(), index); + await _audioHandler.customAction('addQueueItemAt', + {'mediaItem': episodeNew.toMediaItem(), 'index': index}); } _queue.addToPlayListAt(episodeNew, index); await updatePlaylist(_queue, updateEpisodes: false); @@ -675,7 +665,7 @@ class AudioPlayerNotifier extends ChangeNotifier { var episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl); _playlist.updateEpisode(episodeNew); if (_playerRunning) { - await AudioService.updateMediaItem(episodeNew.toMediaItem()); + await _audioHandler.updateMediaItem(episodeNew.toMediaItem()); } } } @@ -683,7 +673,7 @@ class AudioPlayerNotifier extends ChangeNotifier { Future delFromPlaylist(EpisodeBrief episode) async { var episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl); if (playerRunning && _playlist.isQueue) { - await AudioService.removeQueueItem(episodeNew.toMediaItem()); + await _audioHandler.removeQueueItem(episodeNew.toMediaItem()); } var index = _queue.delFromPlaylist(episodeNew); if (index == 0) { @@ -702,8 +692,9 @@ class AudioPlayerNotifier extends ChangeNotifier { _queue.addToPlayListAt(episode, newIndex); updatePlaylist(_queue, updateEpisodes: false); if (playerRunning && _playlist.name == 'Queue') { - await AudioService.removeQueueItem(episode.toMediaItem()); - await AudioService.addQueueItemAt(episode.toMediaItem(), newIndex); + await _audioHandler.removeQueueItem(episode.toMediaItem()); + await _audioHandler.customAction('addQueueItemAt', + {'mediaItem': episode.toMediaItem(), 'index': newIndex}); } if (newIndex == 0) { _lastPosition = 0; @@ -715,7 +706,8 @@ class AudioPlayerNotifier extends ChangeNotifier { await delFromPlaylist(episode); final episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl); if (_playerRunning && _playlist.isQueue) { - await AudioService.addQueueItemAt(episodeNew.toMediaItem(), 1); + await _audioHandler.customAction( + '', {'mediaItem': episodeNew.toMediaItem(), 'index': 1}); _queue.addToPlayListAt(episode, 1, existed: false); } else { _queue.addToPlayListAt(episode, 0, existed: false); @@ -756,7 +748,7 @@ class AudioPlayerNotifier extends ChangeNotifier { for (var e in episodes) { playlist.addToPlayList(e); if (playerRunning && playlist == _playlist) { - AudioService.addQueueItem(e.toMediaItem()); + _audioHandler.addQueueItem(e.toMediaItem()); } } updatePlaylist(playlist, updateEpisodes: false); @@ -767,7 +759,7 @@ class AudioPlayerNotifier extends ChangeNotifier { for (var e in episodes) { playlist.delFromPlaylist(e); if (playerRunning && playlist == _playlist) { - AudioService.removeQueueItem(e.toMediaItem()); + _audioHandler.removeQueueItem(e.toMediaItem()); } } updatePlaylist(playlist, updateEpisodes: false); @@ -781,8 +773,9 @@ class AudioPlayerNotifier extends ChangeNotifier { if (newIndex > oldIndex) { newIndex -= 1; } - await AudioService.removeQueueItem(episode.toMediaItem()); - await AudioService.addQueueItemAt(episode.toMediaItem(), newIndex); + await _audioHandler.removeQueueItem(episode.toMediaItem()); + await _audioHandler.customAction('addQueueItemAt', + {'mediaItem': episode.toMediaItem(), 'index': newIndex}); } updatePlaylist(playlist, updateEpisodes: false); } @@ -836,88 +829,85 @@ class AudioPlayerNotifier extends ChangeNotifier { /// Audio control. Future pauseAduio() async { - await AudioService.pause(); + await _audioHandler.pause(); } Future resumeAudio() async { _remoteErrorMessage = null; notifyListeners(); - if (_audioState != AudioProcessingState.connecting && - _audioState != AudioProcessingState.none) { - AudioService.play(); + if (_audioState != AudioProcessingState.loading) { + _audioHandler.play(); } } Future playNext() async { _remoteErrorMessage = null; - await AudioService.skipToNext(); + await _audioHandler.skipToNext(); notifyListeners(); } Future forwardAudio(int s) async { var pos = _backgroundAudioPosition + s * 1000; - await AudioService.seekTo(Duration(milliseconds: pos)); + await _audioHandler.seek(Duration(milliseconds: pos)); } Future fastForward() async { - await AudioService.fastForward(); + await _audioHandler.fastForward(); } Future rewind() async { - await AudioService.rewind(); + await _audioHandler.rewind(); } Future seekTo(int position) async { - if (_audioState != AudioProcessingState.connecting && - _audioState != AudioProcessingState.none) { - await AudioService.seekTo(Duration(milliseconds: position)); + if (_audioState != AudioProcessingState.loading) { + await _audioHandler.seek(Duration(milliseconds: position)); } } Future sliderSeek(double val) async { - if (_audioState != AudioProcessingState.connecting && - _audioState != AudioProcessingState.none) { + if (_audioState != AudioProcessingState.loading) { _noSlide = false; _seekSliderValue = val; notifyListeners(); _currentPosition = (val * _backgroundAudioDuration).toInt(); - await AudioService.seekTo(Duration(milliseconds: _currentPosition)); + await _audioHandler.seek(Duration(milliseconds: _currentPosition)); _noSlide = true; } } /// Set player speed. Future setSpeed(double speed) async { - await AudioService.customAction('setSpeed', speed); + await _audioHandler.customAction('setSpeed', {'speed': speed}); _currentSpeed = speed; await _speedStorage.saveDouble(_currentSpeed); notifyListeners(); } /// Set skip silence. - Future setSkipSilence({@required bool skipSilence}) async { - await AudioService.customAction('setSkipSilence', skipSilence); - _skipSilence = skipSilence; - await _skipSilenceStorage.saveBool(_skipSilence); - notifyListeners(); - } + // Future setSkipSilence({@required bool skipSilence}) async { + // await AudioService.customAction('setSkipSilence', skipSilence); + // _skipSilence = skipSilence; + // await _skipSilenceStorage.saveBool(_skipSilence); + // notifyListeners(); + // } set setVolumeGain(int volumeGain) { _volumeGain = volumeGain; if (_playerRunning && _boostVolume) { - setBoostVolume(boostVolume: _boostVolume, gain: _volumeGain); + // setBoostVolume(boostVolume: _boostVolume, gain: _volumeGain); } notifyListeners(); _volumeGainStorage.saveInt(volumeGain); } - Future setBoostVolume({@required bool boostVolume, int gain}) async { - await AudioService.customAction( - 'setBoostVolume', [boostVolume, _volumeGain]); - _boostVolume = boostVolume; - notifyListeners(); - await _boostVolumeStorage.saveBool(boostVolume); - } + // Future setBoostVolume({@required bool boostVolume, int gain}) async { + // await _audioHandler.customAction( + // 'setBoostVolume', [boostVolume, _volumeGain]); + // _boostVolume = boostVolume; + // notifyListeners(); + // await _boostVolumeStorage.saveBool(boostVolume); + // } //Set sleep timer void sleepTimer(int mins) { @@ -940,7 +930,7 @@ class AudioPlayerNotifier extends ChangeNotifier { _startSleepTimer = false; _switchValue = 0; if (_playerRunning) { - AudioService.stop(); + _audioHandler.stop(); } notifyListeners(); // AudioService.disconnect(); @@ -950,7 +940,7 @@ class AudioPlayerNotifier extends ChangeNotifier { _switchValue = 1; notifyListeners(); if (_queue.episodes.length > 1 && _autoPlay) { - AudioService.customAction('stopAtEnd'); + _audioHandler.customAction('stopAtEnd', {}); } } } @@ -969,7 +959,7 @@ class AudioPlayerNotifier extends ChangeNotifier { _switchValue = 0; notifyListeners(); } else if (_sleepTimerMode == SleepTimerMode.endOfEpisode) { - AudioService.customAction('cancelStopAtEnd'); + _audioHandler.customAction('cancelStopAtEnd', {}); _switchValue = 0; _stopOnComplete = false; notifyListeners(); @@ -977,81 +967,62 @@ class AudioPlayerNotifier extends ChangeNotifier { } } -class AudioPlayerTask extends BackgroundAudioTask { +class CustomAudioHandler extends BaseAudioHandler + with QueueHandler, SeekHandler { final cacheStorage = KeyValueStorage(cacheMaxKey); final layoutStorage = KeyValueStorage(notificationLayoutKey); - final List _queue = []; - final AudioPlayer _audioPlayer = AudioPlayer(); + final AudioPlayer _player = AudioPlayer(); AudioSession _session; - AudioProcessingState _skipState; bool _playing; bool _interrupted = false; bool _stopAtEnd; int _cacheMax; - int _index = 0; bool _isQueue; - bool get hasNext => _queue.length > 0; + int _index = 0; - MediaItem get mediaItem => hasNext ? _queue[_index] : null; + bool get hasNext => queue.value.length > 0; + MediaItem get currentMediaItem => mediaItem.value; + bool get playing => playbackState.value.playing; - StreamSubscription _playerStateSubscription; - StreamSubscription _eventSubscription; + // MediaItem get _currentMediaItem => hasNext ? _queue[_index] : null; + BehaviorSubject> customEvent; - @override - Future onStart(Map params) async { - _stopAtEnd = false; - _session = await AudioSession.instance; - _index = params['index'] ?? 0; - _isQueue = params['isQueue'] ?? true; - await _session.configure(AudioSessionConfiguration.speech()); - _handleInterruption(_session); - _playerStateSubscription = _audioPlayer.playbackStateStream - .where((state) => state == AudioPlaybackState.completed) - .listen((state) { - _handlePlaybackCompleted(); - }); - - _eventSubscription = _audioPlayer.playbackEventStream.listen((event) { - if (event.playbackError != null) { - _playing = false; - _setState(processingState: _skipState ?? AudioProcessingState.error); - } - final bufferingState = - event.buffering ? AudioProcessingState.buffering : null; - switch (event.state) { - case AudioPlaybackState.paused: - _setState( - processingState: bufferingState ?? AudioProcessingState.ready, - position: event.position, - ); - break; - case AudioPlaybackState.playing: - _setState( - processingState: bufferingState ?? AudioProcessingState.ready, - position: event.position, - ); - break; - case AudioPlaybackState.connecting: - _setState( - processingState: _skipState ?? AudioProcessingState.connecting, - position: event.position, - ); - break; - default: - break; - } + CustomAudioHandler() { + _player.currentIndexStream + .listen((index) => mediaItem.add(queue.value[index])); + _player.playbackEventStream.listen((event) { + playbackState.add(playbackState.value.copyWith( + controls: [ + MediaControl.skipToPrevious, + playing ? MediaControl.pause : MediaControl.play, + MediaControl.skipToNext, + ], + androidCompactActionIndices: [0, 1, 3], + systemActions: { + MediaAction.seek, + MediaAction.seekForward, + MediaAction.seekBackward, + }, + processingState: { + ProcessingState.idle: AudioProcessingState.idle, + ProcessingState.loading: AudioProcessingState.loading, + ProcessingState.buffering: AudioProcessingState.buffering, + ProcessingState.ready: AudioProcessingState.ready, + ProcessingState.completed: AudioProcessingState.completed, + }[_player.processingState], + playing: _player.playing, + updatePosition: _player.position, + bufferedPosition: _player.bufferedPosition, + speed: _player.speed, + )); }); + _configureSession(); } - void _handlePlaybackCompleted() async { - if (hasNext) { - onSkipToNext(); - } else { - _audioPlayer.stop(); - _queue.removeAt(0); - await AudioServiceBackground.setQueue(_queue); - onStop(); - } + Future _configureSession() async { + _session = await AudioSession.instance; + await _session.configure(AudioSessionConfiguration.speech()); + _handleInterruption(_session); } void _handleInterruption(AudioSession session) async { @@ -1060,19 +1031,19 @@ class AudioPlayerTask extends BackgroundAudioTask { switch (event.type) { case AudioInterruptionType.pause: if (_playing) { - onPause(); + pause(); _interrupted = true; } break; case AudioInterruptionType.duck: if (_playing) { - onPause(); + pause(); _interrupted = true; } break; case AudioInterruptionType.unknown: if (_playing) { - onPause(); + pause(); _interrupted = true; } break; @@ -1081,12 +1052,12 @@ class AudioPlayerTask extends BackgroundAudioTask { switch (event.type) { case AudioInterruptionType.pause: if (!_playing && _interrupted) { - onPlay(); + play(); } break; case AudioInterruptionType.duck: if (!_playing && _interrupted) { - onPlay(); + play(); } break; case AudioInterruptionType.unknown: @@ -1096,212 +1067,149 @@ class AudioPlayerTask extends BackgroundAudioTask { } }); session.becomingNoisyEventStream.listen((_) { - if (_playing) onPause(); + if (_playing) pause(); }); } + void _handlePlaybackCompleted() async { + if (hasNext) { + skipToNext(); + } else { + _player.stop(); + removeQueueItemAt(0); + stop(); + } + } + void playPause() { - if (AudioServiceBackground.state.playing) { - onPause(); + if (playbackState.value.playing) { + pause(); } else { - onPlay(); + play(); } } - @override - Future onSkipToNext() async { - _skipState = AudioProcessingState.skippingToNext; - _playing = false; - await _audioPlayer.stop(); - AudioServiceBackground.sendCustomEvent(mediaItem.title); + Future skipToNext() async { + customEvent.add({'mediaItem': currentMediaItem.title}); if (_isQueue) { - if (_queue.length > 0) { - _queue.removeAt(0); - } - await AudioServiceBackground.setQueue(_queue); - } else { - if (_index == _queue.length - 1) { - _index = 0; - } else { - _index += 1; + if (queue.value.length > 0) { + removeQueueItemAt(0); } } - - if (_queue.length == 0 || _stopAtEnd) { - _skipState = null; + if (queue.value.length == 0 || _stopAtEnd) { await Future.delayed(Duration(milliseconds: 200)); - await onStop(); + await stop(); } else { - // await AudioServiceBackground.setQueue(_queue); - await AudioServiceBackground.setMediaItem(mediaItem); - await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax); - var duration = await _audioPlayer.durationFuture; + super.skipToNext(); + var duration = await _player.durationFuture; if (duration != null) { - await AudioServiceBackground.setMediaItem( - mediaItem.copyWith(duration: duration)); + mediaItem.add(currentMediaItem.copyWith(duration: duration)); } - _skipState = null; - _playFromStart(); } } - @override - Future onPlay() async { - if (_skipState == null) { - if (_playing == null) { - _playing = true; - _cacheMax = await cacheStorage.getInt( - defaultValue: (200 * 1024 * 1024).toInt()); - if (_cacheMax == 0) { - await cacheStorage.saveInt((200 * 1024 * 1024).toInt()); - _cacheMax = 200 * 1024 * 1024; - } - await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax); - var duration = await _audioPlayer.durationFuture; - if (duration != null) { - await AudioServiceBackground.setMediaItem( - mediaItem.copyWith(duration: duration)); - } - _playFromStart(); - } else { - _playing = true; - _session.setActive(true); - if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting || - _audioPlayer.playbackEvent.state != AudioPlaybackState.none) { - await _audioPlayer.play(); - await _seekRelative(Duration(seconds: -3)); - } + Future play() async { + if (playing == null) { + // _cacheMax = await cacheStorage.getInt( + // defaultValue: (200 * 1024 * 1024).toInt()); + // if (_cacheMax == 0) { + // await cacheStorage.saveInt((200 * 1024 * 1024).toInt()); + // _cacheMax = 200 * 1024 * 1024; + // } + await _player.setUrl(queue.value[_index].id); + var duration = await _player.durationFuture; + if (duration != null) { + mediaItem.add(currentMediaItem.copyWith(duration: duration)); } + _playFromStart(); + } else { + _session.setActive(true); + await _player.play(); + await _seekRelative(Duration(seconds: -3)); } } Future _playFromStart() async { _playing = true; _session.setActive(true); - if (mediaItem.extras['skipSecondsStart'] > 0 || - mediaItem.extras['skipSecondsEnd'] > 0) { - _audioPlayer - .seek(Duration(seconds: mediaItem.extras['skipSecondsStart'])); + if (currentMediaItem.extras['skipSecondsStart'] > 0 || + currentMediaItem.extras['skipSecondsEnd'] > 0) { + _player + .seek(Duration(seconds: mediaItem.value.extras['skipSecondsStart'])); } - if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting || - _audioPlayer.playbackEvent.state != AudioPlaybackState.none) { - try { - _audioPlayer.play(); - } catch (e) { - _setState(processingState: AudioProcessingState.error); - } + try { + _player.play(); + } catch (e) { + playbackState.add(playbackState.value + .copyWith(processingState: AudioProcessingState.error)); } } - @override - Future onPause() async { - if (_skipState == null) { - if (_playing == null) { - } else if (_playing) { - _playing = false; - _audioPlayer.pause(); - } - } + Future pause() async { + _player.pause(); } - @override - Future onSeekTo(Duration position) async { - if (_audioPlayer.playbackEvent.state != AudioPlaybackState.connecting || - _audioPlayer.playbackEvent.state != AudioPlaybackState.none) { - await _audioPlayer.seek(position); - } + Future seekTo(Duration position) async { + await _player.seek(position); } - @override Future onClick(MediaButton button) async { switch (button) { case MediaButton.media: - if (AudioServiceBackground.state?.playing == true) { - await onPause(); + if (playing) { + await pause(); } else { - await onPlay(); + await play(); } break; case MediaButton.next: - await onFastForward(); + await fastForward(); break; case MediaButton.previous: - await onRewind(); + await rewind(); break; } } Future _seekRelative(Duration offset) async { - var newPosition = _audioPlayer.playbackEvent.position + offset; + var newPosition = playbackState.value.position + offset; if (newPosition < Duration.zero) newPosition = Duration.zero; - onSeekTo(newPosition); + seekTo(newPosition); } - @override - Future onStop() async { - await _audioPlayer.stop(); - await _audioPlayer.dispose(); - _playing = false; - _playerStateSubscription.cancel(); - _eventSubscription.cancel(); - await _setState(processingState: AudioProcessingState.none); - AudioServiceBackground.sendCustomEvent({'playerRunning': false}); - await super.onStop(); + Future stop() async { + await _player.stop(); + await _player.dispose(); + playbackState.add(playbackState.value + .copyWith(processingState: AudioProcessingState.loading)); + customEvent.add({'playerRunning': false}); + await super.stop(); } - @override - Future onTaskRemoved() async { - await onStop(); + Future taskRemoved() async { + await stop(); } - @override - Future onAddQueueItem(MediaItem mediaItem) async { - _queue.add(mediaItem); - await AudioServiceBackground.setQueue(_queue); - } - - @override - Future onRemoveQueueItem(MediaItem mediaItem) async { - var index = _queue.indexOf(mediaItem); - if (index < _index) _index -= 1; - _queue.removeWhere((item) => item.id == mediaItem.id); - await AudioServiceBackground.setQueue(_queue); - } - - @override - Future onAddQueueItemAt(MediaItem mediaItem, int index) async { + Future _addQueueItemAt(MediaItem item, int index) async { if (index == 0 && _isQueue) { - await _audioPlayer.stop(); - _queue.removeAt(0); - _queue.removeWhere((item) => item.id == mediaItem.id); - _queue.insert(0, mediaItem); - await AudioServiceBackground.setQueue(_queue); - await AudioServiceBackground.setMediaItem(mediaItem); - await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax); - var duration = await _audioPlayer.durationFuture ?? Duration.zero; - AudioServiceBackground.setMediaItem( - mediaItem.copyWith(duration: duration)); + await _player.stop(); + queue.add(queue.value..removeAt(0)); + queue.add(queue.value..removeWhere((i) => i.id == item.id)); + queue.add(queue.value..insert(index, item)); + // await _player.setUrl(mediaItem.id, cacheMax: _cacheMax); + await _player.setUrl(item.id); + mediaItem.add(item); + var duration = await _player.durationFuture ?? Duration.zero; + mediaItem.add(mediaItem.value.copyWith(duration: duration)); _playFromStart(); //onPlay(); } else { - _queue.insert(index, mediaItem); - //if (index < _index) _index += 1; - await AudioServiceBackground.setQueue(_queue); + queue.add(queue.value..insert(index, item)); } } @override - Future onFastForward() async { - await _seekRelative(fastForwardInterval); - } - - @override - Future onRewind() async { - await _seekRelative(-rewindInterval); - } - - @override - Future onCustomAction(funtion, argument) async { + Future customAction(funtion, argument) async { switch (funtion) { case 'stopAtEnd': _stopAtEnd = true; @@ -1310,86 +1218,84 @@ class AudioPlayerTask extends BackgroundAudioTask { _stopAtEnd = false; break; case 'setSpeed': - await _audioPlayer.setSpeed(argument); + await _player.setSpeed(argument['speed']); break; case 'setSkipSilence': - await _setSkipSilence(argument); + // await _skipSilence(argument); break; case 'setBoostVolume': - await _setBoostVolume(argument[0], argument[1]); + // await _setBoostVolume(argument[0], argument[1]); break; case 'setIsQueue': - _isQueue = argument; + _isQueue = argument['isQueue']; break; case 'changeQueue': - await _changeQueue(argument); + await _changeQueue(argument['queue']); break; case 'changeIndex': - await _changeIndex(argument); + await _changeIndex(argument['index']); break; + case 'addQueueItemAt': + await _addQueueItemAt(argument['mediaItem'], argument['index']); } } - Future _changeQueue(List items) async { - var queue = [for (var i in items) MediaItem.fromJson(json.decode(i))]; - await _audioPlayer.stop(); - AudioServiceBackground.sendCustomEvent(mediaItem.title); - _queue.clear(); - _queue.addAll(queue); + Future _changeQueue(List newQueue) async { + await _player.stop(); + customEvent.add({'mediaItem': currentMediaItem.title}); + queue.add(newQueue); _index = 0; - await AudioServiceBackground.setQueue(_queue); - await AudioServiceBackground.setMediaItem(mediaItem); - await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax); - var duration = await _audioPlayer.durationFuture ?? Duration.zero; - AudioServiceBackground.setMediaItem(mediaItem.copyWith(duration: duration)); + await _player.setUrl(currentMediaItem.id); + var duration = await _player.durationFuture ?? Duration.zero; + mediaItem.add(currentMediaItem.copyWith(duration: duration)); _playFromStart(); } Future _changeIndex(int index) async { - await _audioPlayer.stop(); - AudioServiceBackground.sendCustomEvent(mediaItem.title); + await _player.stop(); + customEvent.add({'mediaItem': currentMediaItem.title}); _index = index; - await AudioServiceBackground.setMediaItem(mediaItem); - await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax); - var duration = await _audioPlayer.durationFuture ?? Duration.zero; - AudioServiceBackground.setMediaItem(mediaItem.copyWith(duration: duration)); + mediaItem.add(currentMediaItem); + await _player.setUrl(currentMediaItem.id); + var duration = await _player.durationFuture ?? Duration.zero; + mediaItem.add(currentMediaItem.copyWith(duration: duration)); _playFromStart(); } - Future _setSkipSilence(bool boo) async { - await _audioPlayer.setSkipSilence(boo); - var duration = await _audioPlayer.durationFuture ?? Duration.zero; - AudioServiceBackground.setMediaItem(mediaItem.copyWith(duration: duration)); - } + // Future _setSkipSilence(bool boo) async { + // await _audioPlayer.setSkipSilence(boo); + // var duration = await _audioPlayer.durationFuture ?? Duration.zero; + // AudioServiceBackground.setMediaItem(mediaItem.copyWith(duration: duration)); + // } - Future _setBoostVolume(bool boo, int gain) async { - await _audioPlayer.setBoostVolume(boo, gain: gain); - } + // Future _setBoostVolume(bool boo, int gain) async { + // await _audioPlayer.setBoostVolume(boo, gain: gain); + // } - Future _setState({ - AudioProcessingState processingState, - Duration position, - Duration bufferedPosition, - }) async { - if (position == null) { - position = _audioPlayer.playbackEvent.position; - } - final index = await layoutStorage.getInt(defaultValue: 0); - await AudioServiceBackground.setState( - controls: _getControls(index), - systemActions: [ - MediaAction.seekTo, - MediaAction.seekForward, - MediaAction.seekBackward, - ], - processingState: - processingState ?? AudioServiceBackground.state.processingState, - playing: _playing ?? false, - position: position, - bufferedPosition: bufferedPosition ?? position, - speed: _audioPlayer.speed, - ); - } + // Future _setState({ + // AudioProcessingState processingState, + // Duration position, + // Duration bufferedPosition, + // }) async { + // if (position == null) { + // position = _player.playbackEvent.position; + // } + // final index = await layoutStorage.getInt(defaultValue: 0); + // await playbackState.add( + // controls: _getControls(index), + // systemActions: [ + // MediaAction.seekTo, + // MediaAction.seekForward, + // MediaAction.seekBackward, + // ], + // processingState: + // processingState ?? AudioServiceBackground.state.processingState, + // playing: _playing ?? false, + // position: position, + // bufferedPosition: bufferedPosition ?? position, + // speed: _audioPlayer.speed, + // ); + // } List _getControls(int index) { switch (index) { diff --git a/lib/type/episodebrief.dart b/lib/type/episodebrief.dart index 31a6329..48d75e7 100644 --- a/lib/type/episodebrief.dart +++ b/lib/type/episodebrief.dart @@ -56,7 +56,7 @@ class EpisodeBrief extends Equatable { artist: feedTitle, album: feedTitle, duration: Duration.zero, - artUri: imagePath == '' ? episodeImage : 'file://$imagePath', + artUri: Uri.parse(imagePath == '' ? episodeImage : 'file://$imagePath'), extras: { 'skipSecondsStart': skipSecondsStart, 'skipSecondsEnd': skipSecondsEnd diff --git a/pubspec.yaml b/pubspec.yaml index 4936b5b..6208f27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter_localizations: sdk: flutter auto_animated: ^3.0.0 - audio_session: ^0.0.9 + audio_session: ^0.1.0 cached_network_image: ^2.5.1 color_thief_flutter: ^1.0.2 confetti: ^0.5.5 @@ -44,22 +44,21 @@ dependencies: json_annotation: ^3.1.1 permission_handler: ^5.0.1 provider: ^4.3.2 - rxdart: ^0.24.1 - sqflite: ^1.3.2+1 + sqflite: ^2.0.0+2 state_notifier: ^0.6.0 tuple: ^1.0.3 url_launcher: ^5.7.10 workmanager: ^0.2.3 wc_flutter_share: ^0.2.2 + just_audio: ^0.7.3 + audio_service: + git: + url: https://github.com/ryanheise/audio_service.git + ref: one-isolate + path: audio_service flutter_downloader: git: url: https://github.com/stonega/flutter_downloader.git - audio_service: - git: - url: https://github.com/stonega/audio_service.git - just_audio: - git: - url: https://github.com/stonega/just_audio.git focused_menu: git: url: https://github.com/stonega/focused_menu.git @@ -73,8 +72,10 @@ dependency_overrides: cookie_jar: ^2.0.0 uuid: ^3.0.1 http: ^0.13.0 - flutter_cache_manager: ^2.1.2 + flutter_cache_manager: ^3.0.1 + rxdart: ^0.26.0 shared_preferences: 2.0.0 + plugin_platform_interface: ^1.0.1 convert: ^3.0.0 xml: ^5.0.2 linkify: