diff --git a/android/app/build.gradle b/android/app/build.gradle
index f4e182a..4fa572b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -42,6 +42,7 @@ android {
lintOptions {
disable 'InvalidPackage'
+ checkReleaseBuilds false
}
defaultConfig {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index bfa7fe2..92d03ae 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/android/build.gradle b/android/build.gradle
index fdde64e..94e89a3 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -6,7 +6,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.2'
+ classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index dc10de9..772ece3 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
-distributionSha256Sum=abc10bcedb58806e8654210f96031db541bcd2d6fc3161e81cb0572d6a15e821
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+#distributionSha256Sum=abc10bcedb58806e8654210f96031db541bcd2d6fc3161e81cb0572d6a15e821
diff --git a/lib/home/audioplayer.dart b/lib/home/audioplayer.dart
index a000ddb..5d22288 100644
--- a/lib/home/audioplayer.dart
+++ b/lib/home/audioplayer.dart
@@ -457,18 +457,10 @@ class _PlaylistWidgetState extends State {
children: [
Padding(
padding: EdgeInsets.all(10.0),
- child: ClipRRect(
- borderRadius:
- BorderRadius.all(Radius.circular(15.0)),
- child: SizedBox(
- height: 30.0,
- width: 30.0,
- child: Image(
- image: episodes[index].avatarImage)
- // Image.file(File(
- // "${episodes[index].imagePath}"))
- ),
- ),
+ child: CircleAvatar(
+ radius: 15,
+ backgroundImage:
+ episodes[index].avatarImage),
),
Expanded(
child: Align(
@@ -522,9 +514,9 @@ class _PlaylistWidgetState extends State {
borderRadius: BorderRadius.all(Radius.circular(15)),
onTap: () {
audio.playNext();
- miniPlaylistKey.currentState.removeItem(
- 0, (context, animation) => Container());
- miniPlaylistKey.currentState.insertItem(0);
+ // miniPlaylistKey.currentState.removeItem(
+ // 0, (context, animation) => Container());
+ // miniPlaylistKey.currentState.insertItem(0);
},
child: SizedBox(
height: 30,
@@ -1336,13 +1328,7 @@ class _ControlPanelState extends State
AudioProcessingState
.buffering ||
data.audioState ==
- AudioProcessingState
- .connecting ||
- data.audioState ==
- AudioProcessingState.none ||
- data.audioState ==
- AudioProcessingState
- .skippingToNext
+ AudioProcessingState.loading
? context.s.buffering
: '',
style: TextStyle(
diff --git a/lib/home/pocast_discovery.dart b/lib/home/pocast_discovery.dart
index 28df023..c78ed40 100644
--- a/lib/home/pocast_discovery.dart
+++ b/lib/home/pocast_discovery.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
import 'package:flutter/material.dart';
import 'package:line_icons/line_icons.dart';
import 'package:provider/provider.dart';
@@ -197,16 +199,20 @@ class DiscoveryPageState extends State {
}
Future> _getTopPodcasts({int page}) async {
- if(environment['apiKey'] == '') return [];
+ if (environment['apiKey'] == '') return [];
final searchEngine = ListenNotesSearch();
- var searchResult = await searchEngine.fetchBestPodcast(
- genre: '',
- page: page,
- );
- final podcastTopList =
- searchResult.podcasts.map((e) => e?.toOnlinePodcast).toList();
- _podcastList.addAll(podcastTopList.cast());
- return _podcastList;
+ try {
+ var searchResult = await searchEngine.fetchBestPodcast(
+ genre: '',
+ page: page,
+ );
+ final podcastTopList =
+ searchResult.podcasts.map((e) => e?.toOnlinePodcast).toList();
+ _podcastList.addAll(podcastTopList.cast());
+ return _podcastList;
+ } catch (e) {
+ return [];
+ }
}
Future _getHideDiscovery() async {
@@ -404,7 +410,9 @@ class __TopPodcastListState extends State<_TopPodcastList> {
@override
void initState() {
_page = 1;
- _searchFuture = _getTopPodcasts(genre: widget.genre, page: _page);
+ try {
+ _searchFuture = _getTopPodcasts(genre: widget.genre, page: _page);
+ } catch (e) {}
super.initState();
}
diff --git a/lib/home/search_podcast.dart b/lib/home/search_podcast.dart
index 3eef2f1..8fb4ab6 100644
--- a/lib/home/search_podcast.dart
+++ b/lib/home/search_podcast.dart
@@ -11,6 +11,7 @@ import 'package:flutter_html/flutter_html.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
+import 'package:tsacdop/util/cache_manager.dart';
import 'package:webfeed/webfeed.dart';
import '../.env.dart';
@@ -251,6 +252,7 @@ class _RssResultState extends State {
CachedNetworkImage(
height: 120.0,
width: 120.0,
+ cacheManager: CustomCacheManager(),
fit: BoxFit.fitWidth,
alignment: Alignment.center,
imageUrl: _onlinePodcast.image,
@@ -1356,6 +1358,7 @@ class PodcastAvatar extends StatelessWidget {
fit: BoxFit.fitWidth,
alignment: Alignment.center,
imageUrl: podcast.image,
+ cacheManager: CustomCacheManager(),
placeholderFadeInDuration: Duration.zero,
progressIndicatorBuilder: (context, url, downloadProgress) =>
Container(
diff --git a/lib/playlists/playlist_home.dart b/lib/playlists/playlist_home.dart
index 0e04864..71afbcb 100644
--- a/lib/playlists/playlist_home.dart
+++ b/lib/playlists/playlist_home.dart
@@ -75,8 +75,8 @@ class _PlaylistHomeState extends State {
return AnnotatedRegion(
value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
- Theme.of(context).accentColorBrightness,
- statusBarIconBrightness: Theme.of(context).accentColorBrightness,
+ Theme.of(context).colorScheme.brightness,
+ statusBarIconBrightness: Theme.of(context).colorScheme.brightness,
systemNavigationBarColor: Theme.of(context).primaryColor,
),
child: WillPopScope(
@@ -143,6 +143,9 @@ class _PlaylistHomeState extends State {
playing
? audio.pauseAduio()
: audio.resumeAudio();
+ } else if (data.item1.isEmpty) {
+ Fluttertoast.showToast(
+ msg: 'Playlist is empty');
} else {
context
.read()
@@ -952,15 +955,14 @@ class __NewPlaylistState extends State<_NewPlaylist> {
}
Future _getEpisodeFromFile(String path) async {
- var metadataRetriever = MetadataRetriever();
final fileLength = File(path).statSync().size;
final pubDate = DateTime.now().millisecondsSinceEpoch;
var primaryColor;
var imagePath;
- await metadataRetriever.setFile(File(path));
- if (metadataRetriever.albumArt != null) {
+ var metadata = await MetadataRetriever.fromFile(File(path));
+ if (metadata.albumArt != null) {
final dir = await getApplicationDocumentsDirectory();
- final image = img.decodeImage(metadataRetriever.albumArt);
+ final image = img.decodeImage(metadata.albumArt);
final thumbnail = img.copyResize(image, width: 300);
var uuid = Uuid().v4();
File("${dir.path}/$uuid.png")..writeAsBytesSync(img.encodePng(thumbnail));
@@ -968,7 +970,6 @@ class __NewPlaylistState extends State<_NewPlaylist> {
primaryColor = await _getColor(File(imagePath));
}
final fileName = path.split('/').last;
- final metadata = await metadataRetriever.metadata;
return EpisodeBrief(
fileName,
'file://$path',
diff --git a/lib/playlists/playlist_page.dart b/lib/playlists/playlist_page.dart
index 8f3d365..b49b862 100644
--- a/lib/playlists/playlist_page.dart
+++ b/lib/playlists/playlist_page.dart
@@ -2,7 +2,6 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-import 'package:file_picker/file_picker.dart';
import '../state/audio_state.dart';
import '../type/episodebrief.dart';
diff --git a/lib/service/gpodder_api.dart b/lib/service/gpodder_api.dart
index cdd780e..a360d53 100644
--- a/lib/service/gpodder_api.dart
+++ b/lib/service/gpodder_api.dart
@@ -31,13 +31,15 @@ class Gpodder {
Future _initDio() async {
final dir = await getApplicationDocumentsDirectory();
- var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
+ var cookieJar =
+ PersistCookieJar(storage: FileStorage("${dir.path}/.cookies/"));
_dio.interceptors.add(CookieManager(cookieJar));
}
Future login({String username, String password}) async {
final dir = await getApplicationDocumentsDirectory();
- var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
+ var cookieJar =
+ PersistCookieJar(storage: FileStorage("${dir.path}/.cookies/"));
cookieJar.delete(Uri.parse(_baseUrl));
_dio.interceptors.add(CookieManager(cookieJar));
final basicAuth =
@@ -81,7 +83,8 @@ class Gpodder {
Future _initService() async {
final dir = await getApplicationDocumentsDirectory();
- var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
+ var cookieJar =
+ PersistCookieJar(storage: FileStorage("${dir.path}/.cookies/"));
cookieJar.delete(Uri.parse(_baseUrl));
await _storage.clearList();
await _addStorage.clearList();
diff --git a/lib/settings/history.dart b/lib/settings/history.dart
index 398e1cf..3955f44 100644
--- a/lib/settings/history.dart
+++ b/lib/settings/history.dart
@@ -441,7 +441,7 @@ class HistoryChart extends StatelessWidget {
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
- getTextStyles: (i) => TextStyle(
+ getTextStyles: (_, i) => TextStyle(
color: const Color(0xff67727d),
fontWeight: FontWeight.bold,
fontSize: 12,
@@ -456,7 +456,7 @@ class HistoryChart extends StatelessWidget {
),
leftTitles: SideTitles(
showTitles: true,
- getTextStyles: (s) => TextStyle(
+ getTextStyles: (_, s) => TextStyle(
color: const Color(0xff67727d),
fontWeight: FontWeight.bold,
fontSize: 12,
diff --git a/lib/state/audio_state.dart b/lib/state/audio_state.dart
index a33967e..7ccffef 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 'dart:developer';
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 }
@@ -79,6 +75,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
KeyValueStorage(markListenedAfterSkipKey);
final _playlistsStorgae = KeyValueStorage(playlistsAllKey);
final _playerStateStorage = KeyValueStorage(playerStateKey);
+ final cacheStorage = KeyValueStorage(cacheMaxKey);
/// Playing episdoe.
EpisodeBrief _episode;
@@ -93,7 +90,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,19 +175,41 @@ class AudioPlayerNotifier extends ChangeNotifier {
// Tmep episode list, playing from search result
List _playFromSearchList = [];
+ AudioHandler _audioHandler;
+
+ StreamSubscription _mediaitemSubscription;
+ StreamSubscription _playbackStateSubscription;
+ StreamSubscription _customEventSubscription;
+
@override
- void addListener(VoidCallback listener) {
+ void addListener(VoidCallback listener) async {
+ await _initAudioData();
+ final cacheMax =
+ await cacheStorage.getInt(defaultValue: (1024 * 1024 * 200).toInt());
+ _audioHandler = await AudioService.init(
+ builder: () => CustomAudioHandler(cacheMax), config: _config);
super.addListener(listener);
- _initAudioData();
- AudioService.connect();
}
@override
- void dispose() async {
- await AudioService.disconnect();
+ void dispose() {
+ _mediaitemSubscription?.cancel();
+ _playbackStateSubscription?.cancel();
+ _customEventSubscription?.cancel();
super.dispose();
}
+ /// Audio service config
+ AudioServiceConfig get _config => AudioServiceConfig(
+ androidNotificationChannelName: 'Tsacdop',
+ androidNotificationIcon: 'drawable/ic_notification',
+ // androidEnableQueue: true,
+ androidStopForegroundOnPause: true,
+ preloadArtwork: false,
+ fastForwardInterval: Duration(seconds: _fastForwardSeconds),
+ rewindInterval: Duration(seconds: _rewindSeconds),
+ );
+
/// Audio playing state.
AudioProcessingState get audioState => _audioState;
int get backgroundAudioDuration => _backgroundAudioDuration;
@@ -247,6 +266,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
_skipSilence = await _skipSilenceStorage.getBool(defaultValue: false);
_boostVolume = await _boostVolumeStorage.getBool(defaultValue: false);
_volumeGain = await _volumeGainStorage.getInt(defaultValue: 3000);
+ _fastForwardSeconds =
+ await _fastForwardSecondsStorage.getInt(defaultValue: 30);
+ _rewindSeconds = await _rewindSecondsStorage.getInt(defaultValue: 30);
}
Future _savePlayerHeight() async {
@@ -326,7 +348,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;
@@ -335,6 +357,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
_startAudioService(_playlist,
position: _lastPosition ?? 0,
index: _playlist.episodes.indexOf(_episode));
+ } else {
+ log('Playlist is empty');
}
}
@@ -347,15 +371,16 @@ 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.isQueue});
+ _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);
@@ -372,7 +397,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
} else {
episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
}
- //TODO load episode from last position when player running
+ // @TODO load episode from last position when player running
if (playerRunning) {
if (_playFromSearchList.contains(_episode)) {
_queue.delFromPlaylist(_episode);
@@ -383,16 +408,17 @@ 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())
- ]);
+ if (_playlist.isQueue) {
+ _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 +443,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 +453,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,26 +463,18 @@ 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.addQueueItems(
+ ([for (var episode in playlist.episodes) episode.toMediaItem()]));
} else {
- await AudioService.addQueueItem(playlist.episodes[index].toMediaItem());
+ await _audioHandler
+ .addQueueItems([playlist.episodes[index].toMediaItem()]);
}
+
+ await _audioHandler.play();
//Check auto sleep timer setting
await _getAutoSleepTimer();
if (_autoSleepTimer) {
@@ -481,58 +494,62 @@ class AudioPlayerNotifier extends ChangeNotifier {
}
}
+ /// Set if playlist is queue.
+ await _audioHandler
+ .customAction('setIsQueue', {'isQueue': playlist.isQueue});
+
/// 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': skipSilence});
}
/// Set boostValome.
if (_boostVolume) {
- await AudioService.customAction(
- 'setBoostVolume', [_boostVolume, _volumeGain]);
+ await _audioHandler.customAction(
+ 'setBoostVolume', {'boostVolume': _boostVolume, 'gain': _volumeGain});
}
- await AudioService.play();
+ _audioHandler.play();
- AudioService.currentMediaItemStream
- .where((event) => event != null)
- .listen((item) async {
- var episode = await _dbHelper.getRssItemWithMediaId(item.id);
- if (episode == null) {
- episode = _playFromSearchList.firstWhere((e) => e.mediaId == item.id,
- orElse: () => null);
- }
- _backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
- if (episode != null) {
- _episode = episode;
- _backgroundAudioDuration = item.duration.inMilliseconds ?? 0;
- if (position > 0 &&
- _backgroundAudioDuration > 0 &&
- _episode.enclosureUrl == _playlist.episodeList[index]) {
- await AudioService.seekTo(Duration(milliseconds: position));
- position = 0;
+ _mediaitemSubscription =
+ _audioHandler.mediaItem.where((event) => event != null).listen(
+ (item) async {
+ var episode = await _dbHelper.getRssItemWithMediaId(item.id);
+ if (episode == null) {
+ episode = _playFromSearchList.firstWhere((e) => e.mediaId == item.id,
+ orElse: () => null);
}
- notifyListeners();
- } else {
- AudioService.skipToNext();
- }
- });
- AudioService.playbackStateStream
- .distinct()
+ if (episode != null) {
+ _episode = episode;
+ _backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
+ if (position > 0 &&
+ _backgroundAudioDuration > 0 &&
+ _episode.enclosureUrl == _playlist.episodeList[index]) {
+ await _audioHandler.seek(Duration(milliseconds: position));
+ position = 0;
+ }
+ notifyListeners();
+ } else {
+ _audioHandler.skipToNext();
+ }
+ },
+ );
+
+ _playbackStateSubscription = _audioHandler.playbackState
.where((event) => event != null)
.listen((event) async {
_current = DateTime.now();
_audioState = event.processingState;
_playing = event?.playing;
_currentSpeed = event.speed;
- _currentPosition = event.currentPosition.inMilliseconds ?? 0;
-
- if (_audioState == AudioProcessingState.stopped) {
+ _currentPosition = event.updatePosition.inMilliseconds ?? 0;
+ if (_audioState == AudioProcessingState.completed) {
if (_switchValue > 0) _switchValue = 0;
}
@@ -548,11 +565,15 @@ class AudioPlayerNotifier extends ChangeNotifier {
notifyListeners();
});
- AudioService.customEventStream.distinct().listen((event) async {
- if (event is String) {
+ _customEventSubscription =
+ _audioHandler.customEvent.distinct().listen((event) async {
+ if (event is Map && event['removePlayed'] != null) {
+ log(event.toString());
+ log(_queue.episodes.first.title);
if (_playlist.isQueue &&
_queue.isNotEmpty &&
- _queue.episodes.first.title == event) {
+ _queue.episodes.first.title == event['removePlayed']) {
+ log(event['removePlayed']);
_queue.delFromPlaylist(_episode);
updatePlaylist(_queue, updateEpisodes: false);
}
@@ -579,25 +600,14 @@ class AudioPlayerNotifier extends ChangeNotifier {
_lastPosition ~/ 1000, _seekSliderValue);
await _dbHelper.saveHistory(history);
}
- //_episode = null;
- }
- });
-
- //double s = _currentSpeed ?? 1.0;
- var getPosition = 0;
- Timer.periodic(Duration(milliseconds: 500), (timer) {
- var s = _currentSpeed ?? 1.0;
- if (_noSlide) {
- if (_playing && !buffering) {
- getPosition = _currentPosition +
- ((DateTime.now().difference(_current).inMilliseconds) * s)
- .toInt();
- _backgroundAudioPosition =
- math.min(getPosition, _backgroundAudioDuration);
- } else {
- _backgroundAudioPosition = _currentPosition ?? 0;
+ if (_playlist.isEmpty) {
+ _episode = null;
+ _backgroundAudioDuration = 0;
+ _backgroundAudioPosition = 0;
}
-
+ }
+ if (event is Map && event['position'] != null) {
+ _backgroundAudioPosition = event['position'].inMilliseconds;
if (_backgroundAudioDuration != null &&
_backgroundAudioDuration != 0 &&
_backgroundAudioPosition != null) {
@@ -606,22 +616,12 @@ class AudioPlayerNotifier extends ChangeNotifier {
} else {
_seekSliderValue = 0;
}
-
- if (_backgroundAudioPosition > 0 &&
- _backgroundAudioPosition < _backgroundAudioDuration) {
- _lastPosition = _backgroundAudioPosition;
- _playerStateStorage.savePlayerState(
- _playlist.id, _episode.enclosureUrl, _lastPosition);
- }
notifyListeners();
}
- if (_audioState == AudioProcessingState.stopped) {
- timer.cancel();
- }
});
}
- /// Queue management.
+ /// Queue management
Future addToPlaylist(EpisodeBrief episode) async {
var episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
if (episodeNew.isNew == 1) {
@@ -629,7 +629,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 +643,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 +676,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 +684,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 +703,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 +717,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 +759,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 +770,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 +784,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,67 +840,69 @@ 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();
+ if (_playlist.isQueue && _queue.isNotEmpty) {
+ _queue.delFromPlaylist(_episode);
+ updatePlaylist(_queue, updateEpisodes: false);
+ }
+ 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.
+ // Set skip silence.
Future setSkipSilence({@required bool skipSilence}) async {
- await AudioService.customAction('setSkipSilence', skipSilence);
+ await _audioHandler
+ .customAction('setSkipSilence', {'skipSilence': skipSilence});
_skipSilence = skipSilence;
await _skipSilenceStorage.saveBool(_skipSilence);
notifyListeners();
@@ -912,8 +918,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
}
Future setBoostVolume({@required bool boostVolume, int gain}) async {
- await AudioService.customAction(
- 'setBoostVolume', [boostVolume, _volumeGain]);
+ await _audioHandler.customAction(
+ 'setBoostVolume', {'boostVolume': boostVolume, 'gain': _volumeGain});
_boostVolume = boostVolume;
notifyListeners();
await _boostVolumeStorage.saveBool(boostVolume);
@@ -940,7 +946,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
_startSleepTimer = false;
_switchValue = 0;
if (_playerRunning) {
- AudioService.stop();
+ _audioHandler.stop();
}
notifyListeners();
// AudioService.disconnect();
@@ -950,7 +956,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
_switchValue = 1;
notifyListeners();
if (_queue.episodes.length > 1 && _autoPlay) {
- AudioService.customAction('stopAtEnd');
+ _audioHandler.customAction('stopAtEnd', {});
}
}
}
@@ -969,7 +975,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,102 +983,118 @@ 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();
- AudioSession _session;
- AudioProcessingState _skipState;
- bool _playing;
+ final AudioPlayer _player = AudioPlayer();
bool _interrupted = false;
- bool _stopAtEnd;
- int _cacheMax;
- int _index = 0;
- bool _isQueue;
- bool get hasNext => _queue.length > 0;
+ int _layoutIndex;
+ bool _stopAtEnd = false;
+ bool _isQueue = false;
+ bool _autoSkip = true;
- MediaItem get mediaItem => hasNext ? _queue[_index] : null;
+ ConcatenatingAudioSource _playlist = ConcatenatingAudioSource(
+ useLazyPreparation: true,
+ shuffleOrder: DefaultShuffleOrder(),
+ children: [],
+ );
- StreamSubscription _playerStateSubscription;
- StreamSubscription _eventSubscription;
+ bool get hasNext => queue.value.length > 0;
+ MediaItem get currentMediaItem => mediaItem.value;
+ bool get playing => playbackState.value.playing;
+
+ PublishSubject