Skip to content

Commit

Permalink
Merge branch 'minhqdao-fix_timers'
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanheise committed Apr 14, 2021
2 parents 2f17cb1 + 72aaeb2 commit e37b50d
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 17 deletions.
5 changes: 5 additions & 0 deletions just_audio/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.7.4

* Fix hang after stopping while loading.
* Fix pending timers error (@minhqdao).

## 0.7.3

* Silence uncaught exceptions in positionStream.
Expand Down
7 changes: 3 additions & 4 deletions just_audio/example/test/widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:just_audio_example/main.dart';

void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
testWidgets('Verify Playlist', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());

// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
(Widget widget) =>
widget is Text && widget.data!.startsWith('Playlist'),
),
findsOneWidget,
);
Expand Down
36 changes: 24 additions & 12 deletions just_audio/lib/just_audio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class AudioPlayer {
/// [_idlePlatform] otherwise.
late Future<AudioPlayerPlatform> _platform;

/// Reflects the current platform immediately after it is set.
AudioPlayerPlatform? _platformValue;

/// The interface to the native portion of the plugin. This will be disposed
/// and set to `null` when not in use.
Future<AudioPlayerPlatform>? _nativePlatform;
Expand Down Expand Up @@ -177,7 +180,7 @@ class AudioPlayer {
.handleError((Object err, StackTrace stackTrace) {/* noop */}));
_shuffleModeEnabledSubject.add(false);
_loopModeSubject.add(LoopMode.off);
_setPlatformActive(false, force: true);
_setPlatformActive(false, force: true)?.catchError((dynamic e) {});
_sequenceSubject.add(null);
// Respond to changes to AndroidAudioAttributes configuration.
if (androidApplyAudioAttributes) {
Expand Down Expand Up @@ -494,7 +497,7 @@ class AudioPlayer {
return s;
}

Timer currentTimer;
Timer? currentTimer;
StreamSubscription? durationSubscription;
StreamSubscription? playbackEventSubscription;
void yieldPosition(Timer timer) {
Expand All @@ -512,12 +515,13 @@ class AudioPlayer {
controller.close();
return;
}
controller.add(position);
if (playing) {
controller.add(position);
}
}

currentTimer = Timer.periodic(step(), yieldPosition);
durationSubscription = durationStream.listen((duration) {
currentTimer.cancel();
currentTimer?.cancel();
currentTimer = Timer.periodic(step(), yieldPosition);
}, onError: (Object e, StackTrace stackTrace) {});
playbackEventSubscription = playbackEventStream.listen((event) {
Expand Down Expand Up @@ -699,6 +703,10 @@ class AudioPlayer {
.then((response) => response.duration);
final duration = await _durationFuture;
_durationSubject.add(duration);
if (platform != _platformValue) {
// the platform has changed since we started loading, so abort.
throw PlatformException(code: 'abort', message: 'Loading interrupted');
}
// Wait for loading state to pass.
await processingStateStream
.firstWhere((state) => state != ProcessingState.loading);
Expand All @@ -723,7 +731,7 @@ class AudioPlayer {
/// [ProcessingState.idle] state.
Future<Duration?> setClip({Duration? start, Duration? end}) async {
if (_disposed) return null;
_setPlatformActive(true);
_setPlatformActive(true)?.catchError((dynamic e) {});
final duration = await _load(
await _platform,
start == null && end == null
Expand Down Expand Up @@ -781,7 +789,8 @@ class AudioPlayer {
} else {
// If the native platform wasn't already active, activating it will
// implicitly restore the playing state and send a play request.
_setPlatformActive(true, playCompleter: playCompleter);
_setPlatformActive(true, playCompleter: playCompleter)
?.catchError((dynamic e) {});
}
}
} else {
Expand Down Expand Up @@ -997,15 +1006,15 @@ class AudioPlayer {
// This method updates _active and _platform before yielding to the next
// task in the event loop.
_active = active;
final oldPlatformFuture = force ? null : _platform;
final position = this.position;
final currentIndex = this.currentIndex;
final audioSource = _audioSource;
final durationCompleter = Completer<Duration?>();
_platform = Future<AudioPlayerPlatform>(() async {

Future<AudioPlayerPlatform> setPlatform() async {
_playbackEventSubscription?.cancel();
if (!force) {
final oldPlatform = await oldPlatformFuture!;
final oldPlatform = _platformValue!;
if (oldPlatform != _idlePlatform) {
await _disposePlatform(oldPlatform);
}
Expand All @@ -1017,6 +1026,7 @@ class AudioPlayer {
? await (_nativePlatform =
JustAudioPlatform.instance.init(InitRequest(id: _id)))
: _idlePlatform!;
_platformValue = platform;
_playbackEventSubscription =
platform.playbackEventMessageStream.listen((message) {
var duration = message.duration;
Expand Down Expand Up @@ -1088,15 +1098,17 @@ class AudioPlayer {
_InitialSeekValues(position: position, index: currentIndex));
durationCompleter.complete(duration);
} catch (e, stackTrace) {
_audioSource = null;
_setPlatformActive(false)?.catchError((dynamic e) {});
durationCompleter.completeError(e, stackTrace);
}
} else {
durationCompleter.complete(null);
}

return platform;
});
}

_platform = setPlatform();
return durationCompleter.future;
}

Expand Down
2 changes: 1 addition & 1 deletion just_audio/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: just_audio
description: A feature-rich audio player for Flutter. Loop, clip and concatenate any sound from any source (asset/file/URL/stream) in a variety of audio formats with gapless playback.
version: 0.7.3
version: 0.7.4
homepage: https://github.com/ryanheise/just_audio/tree/master/just_audio

environment:
Expand Down
148 changes: 148 additions & 0 deletions just_audio/test/just_audio_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,154 @@ void runTests() {
await player.setUrl('https://bar.bar/foo.mp3');
await player.dispose();
});

test('positionStream emissions: seek while paused', () async {
final player = AudioPlayer();
await player.setUrl('https://bar.bar/foo.mp3');
expectState(
player: player,
position: Duration.zero,
processingState: ProcessingState.ready,
playing: false,
);
var completer = Completer<dynamic>();
late StreamSubscription subscription;
subscription = player.positionStream.listen((position) {
expectDuration(position, Duration.zero);
subscription.cancel();
completer.complete();
});
await completer.future;

final duration1 = Duration(seconds: 1);
final duration2 = Duration(milliseconds: 600);
final duration3 = Duration(milliseconds: 750);

await player.seek(duration1);
expectState(
player: player,
position: duration1,
processingState: ProcessingState.ready,
playing: false,
);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, duration1);
subscription.cancel();
completer.complete();
});
await completer.future;

player.play();
await Future<dynamic>.delayed(duration2);
expectState(
player: player,
position: duration1 + duration2,
processingState: ProcessingState.ready,
playing: true,
);
await player.pause();
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, duration1 + duration2);
subscription.cancel();
completer.complete();
});
await completer.future;

await player.seek(duration1 + duration2 + duration3);
expectState(
player: player,
position: duration1 + duration2 + duration3,
processingState: ProcessingState.ready,
playing: false,
);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, duration1 + duration2 + duration3);
subscription.cancel();
completer.complete();
});
await completer.future;

await player.dispose();
});

test('positionStream emissions: switch audio sources', () async {
final player = AudioPlayer();
final playlist = ConcatenatingAudioSource(
children: [
AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')),
AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')),
],
);
await player.setAudioSource(playlist);
expectState(
player: player,
position: Duration.zero,
processingState: ProcessingState.ready,
playing: false,
);
expect(player.currentIndex, 0);
var completer = Completer<dynamic>();
late StreamSubscription subscription;
subscription = player.positionStream.listen((position) {
expectDuration(position, Duration.zero);
subscription.cancel();
completer.complete();
});
await completer.future;

final duration1 = Duration(seconds: 1);
final duration2 = Duration(seconds: 600);

await player.seek(duration1);
expect(player.currentIndex, 0);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, duration1);
subscription.cancel();
completer.complete();
});
await completer.future;

await player.seekToNext();
expect(player.currentIndex, 1);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, Duration.zero);
subscription.cancel();
completer.complete();
});
await completer.future;

await player.seek(duration1, index: 1);
expect(player.currentIndex, 1);
await player.seekToNext();
// There is no next
expect(player.currentIndex, 1);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
// No position change because there is no next
expectDuration(position, duration1);
subscription.cancel();
completer.complete();
});
await completer.future;

// Switch to index 0 and seek position at the same time
await player.seek(duration2, index: 0);
expect(player.currentIndex, 0);
completer = Completer<dynamic>();
subscription = player.positionStream.listen((position) {
expectDuration(position, duration2);
subscription.cancel();
completer.complete();
});
await completer.future;

await player.dispose();
});
}

class MockJustAudio extends Mock
Expand Down

0 comments on commit e37b50d

Please sign in to comment.