diff --git a/packages/audioplayers/example/integration_test/tabs/controls_tab.dart b/packages/audioplayers/example/integration_test/tabs/controls_tab.dart index 9060674ed..596c885a2 100644 --- a/packages/audioplayers/example/integration_test/tabs/controls_tab.dart +++ b/packages/audioplayers/example/integration_test/tabs/controls_tab.dart @@ -17,8 +17,8 @@ Future testControlsTab( await tester.tap(find.byKey(const Key('controlsTab'))); await tester.pumpAndSettle(); - // Live stream takes some time to get initialized - final timeout = Duration(seconds: audioSourceTestData.isLiveStream ? 8 : 1); + // Sources take some time to get initialized + const timeout = Duration(seconds: 8); if (features.hasVolume) { await tester.testVolume('0.5', timeout: timeout); @@ -75,7 +75,7 @@ Future testControlsTab( await tester.resume(); await tester.pump(const Duration(seconds: 1)); // Test pause - await tester.tap(find.byKey(const Key('control-pause'))); + await tester.scrollToAndTap(const Key('control-pause')); await tester.resume(); await tester.pump(const Duration(seconds: 1)); await tester.stop(); @@ -130,14 +130,14 @@ Future testControlsTab( extension ControlsWidgetTester on WidgetTester { Future resume() async { - await tap(find.byKey(const Key('control-resume'))); + await scrollToAndTap(const Key('control-resume')); await pumpAndSettle(); } Future stop() async { final st = StackTrace.current.toString(); - await tap(find.byKey(const Key('control-stop'))); + await scrollToAndTap(const Key('control-stop')); await waitOneshot(const Key('toast-player-stopped-0'), stackTrace: st); await pumpAndSettle(); } @@ -147,7 +147,7 @@ extension ControlsWidgetTester on WidgetTester { Duration timeout = const Duration(seconds: 1), }) async { printOnFailure('Test Volume: $volume'); - await tap(find.byKey(Key('control-volume-$volume'))); + await scrollToAndTap(Key('control-volume-$volume')); await resume(); // TODO(Gustl22): get volume from native implementation await pump(timeout); @@ -159,7 +159,7 @@ extension ControlsWidgetTester on WidgetTester { Duration timeout = const Duration(seconds: 1), }) async { printOnFailure('Test Balance: $balance'); - await tap(find.byKey(Key('control-balance-$balance'))); + await scrollToAndTap(Key('control-balance-$balance')); await resume(); // TODO(novikov): get balance from native implementation await pump(timeout); @@ -171,7 +171,7 @@ extension ControlsWidgetTester on WidgetTester { Duration timeout = const Duration(seconds: 2), }) async { printOnFailure('Test Rate: $rate'); - await tap(find.byKey(Key('control-rate-$rate'))); + await scrollToAndTap(Key('control-rate-$rate')); await resume(); // TODO(Gustl22): get rate from native implementation await pump(timeout); @@ -185,7 +185,7 @@ extension ControlsWidgetTester on WidgetTester { printOnFailure('Test Seek: $seek'); final st = StackTrace.current.toString(); - await tap(find.byKey(Key('control-seek-$seek'))); + await scrollToAndTap(Key('control-seek-$seek')); await waitOneshot(const Key('toast-seek-complete-0'), stackTrace: st); @@ -198,7 +198,7 @@ extension ControlsWidgetTester on WidgetTester { printOnFailure('Test Player Mode: ${mode.name}'); final st = StackTrace.current.toString(); - await tap(find.byKey(Key('control-player-mode-${mode.name}'))); + await scrollToAndTap(Key('control-player-mode-${mode.name}')); await waitFor( () async => expectEnumToggleHasSelected( const Key('control-player-mode'), @@ -212,7 +212,7 @@ extension ControlsWidgetTester on WidgetTester { printOnFailure('Test Release Mode: ${mode.name}'); final st = StackTrace.current.toString(); - await tap(find.byKey(Key('control-release-mode-${mode.name}'))); + await scrollToAndTap(Key('control-release-mode-${mode.name}')); await waitFor( () async => expectEnumToggleHasSelected( const Key('control-release-mode'), diff --git a/packages/audioplayers/example/integration_test/tabs/source_tab.dart b/packages/audioplayers/example/integration_test/tabs/source_tab.dart index 1bd7c53d0..1185aa467 100644 --- a/packages/audioplayers/example/integration_test/tabs/source_tab.dart +++ b/packages/audioplayers/example/integration_test/tabs/source_tab.dart @@ -22,8 +22,7 @@ extension ControlsWidgetTester on WidgetTester { printOnFailure('Test setting source: $sourceKey'); final st = StackTrace.current.toString(); final sourceWidgetKey = Key('setSource-$sourceKey'); - await scrollTo(sourceWidgetKey); - await tap(find.byKey(sourceWidgetKey)); + await scrollToAndTap(sourceWidgetKey); await waitOneshot(const Key('toast-source-set'), stackTrace: st); } diff --git a/packages/audioplayers/example/integration_test/tabs/stream_tab.dart b/packages/audioplayers/example/integration_test/tabs/stream_tab.dart index b4b18c044..08f91d531 100644 --- a/packages/audioplayers/example/integration_test/tabs/stream_tab.dart +++ b/packages/audioplayers/example/integration_test/tabs/stream_tab.dart @@ -31,11 +31,11 @@ Future testStreamsTab( await tester.testDuration(audioSourceTestData.duration); } - // Live stream takes some time to get initialized - final timeout = Duration(seconds: audioSourceTestData.isLiveStream ? 8 : 1); + // Sources take some time to get initialized + const timeout = Duration(seconds: 8); await tester.pumpAndSettle(); - await tester.tap(find.byKey(const Key('play_button'))); + await tester.scrollToAndTap(const Key('play_button')); await tester.pumpAndSettle(); // Cannot test more precisely as it is dependent on pollInterval @@ -79,7 +79,7 @@ Future testStreamsTab( await tester.testPlayerState(PlayerState.completed); await tester.testOnPlayerState(PlayerState.completed); } else if (audioSourceTestData.duration > const Duration(seconds: 5)) { - await tester.tap(find.byKey(const Key('pause_button'))); + await tester.scrollToAndTap(const Key('pause_button')); await tester.pumpAndSettle(); await tester.testPlayerState(PlayerState.paused); await tester.testOnPlayerState(PlayerState.paused); @@ -141,7 +141,7 @@ extension StreamWidgetTester on WidgetTester { Future stopStream() async { final st = StackTrace.current.toString(); - await tap(find.byKey(const Key('stop_button'))); + await scrollToAndTap(const Key('stop_button')); await waitOneshot(const Key('toast-player-stopped-0'), stackTrace: st); await pumpAndSettle(); } @@ -154,7 +154,7 @@ extension StreamWidgetTester on WidgetTester { final st = StackTrace.current.toString(); await waitFor( () async { - await tap(find.byKey(const Key('getDuration'))); + await scrollToAndTap(const Key('getDuration')); await pump(); expectWidgetHasDuration( const Key('durationText'), @@ -176,7 +176,7 @@ extension StreamWidgetTester on WidgetTester { final st = StackTrace.current.toString(); await waitFor( () async { - await tap(find.byKey(const Key('getPosition'))); + await scrollToAndTap(const Key('getPosition')); await pump(); expectWidgetHasDuration( const Key('positionText'), @@ -196,7 +196,7 @@ extension StreamWidgetTester on WidgetTester { final st = StackTrace.current.toString(); await waitFor( () async { - await tap(find.byKey(const Key('getPlayerState'))); + await scrollToAndTap(const Key('getPlayerState')); await pump(); expectWidgetHasText( const Key('playerStateText'), @@ -210,7 +210,7 @@ extension StreamWidgetTester on WidgetTester { Future testOnDuration( Duration duration, { - Duration timeout = const Duration(seconds: 8), + Duration timeout = const Duration(seconds: 10), }) async { printOnFailure('Test OnDuration: $duration'); final st = StackTrace.current.toString(); @@ -227,7 +227,7 @@ extension StreamWidgetTester on WidgetTester { Future testOnPosition( Duration position, { Matcher Function(Duration) matcher = equals, - Duration timeout = const Duration(seconds: 8), + Duration timeout = const Duration(seconds: 10), }) async { printOnFailure('Test OnPosition: $position'); final st = StackTrace.current.toString(); @@ -244,7 +244,7 @@ extension StreamWidgetTester on WidgetTester { Future testOnPlayerState( PlayerState playerState, { - Duration timeout = const Duration(seconds: 8), + Duration timeout = const Duration(seconds: 10), }) async { printOnFailure('Test OnState: $playerState'); final st = StackTrace.current.toString(); diff --git a/packages/audioplayers/example/integration_test/test_utils.dart b/packages/audioplayers/example/integration_test/test_utils.dart index b1553b967..75199fd41 100644 --- a/packages/audioplayers/example/integration_test/test_utils.dart +++ b/packages/audioplayers/example/integration_test/test_utils.dart @@ -109,12 +109,20 @@ $lastFailureMsg''', } } + Future scrollToAndTap(Key widgetKey) async { + await scrollTo(widgetKey); + await tap(find.byKey(widgetKey)); + } + Future scrollTo(Key widgetKey) async { - await dragUntilVisible( - find.byKey(widgetKey), - find.byType(SingleChildScrollView).first, - const Offset(0, 100), - ); + final finder = find.byKey(widgetKey); + if (finder.hitTestable().evaluate().isEmpty) { + await scrollUntilVisible( + finder, + 100, + scrollable: find.byType(Scrollable).first, + ); + } await pumpAndSettle(); } } diff --git a/packages/audioplayers/example/lib/tabs/streams.dart b/packages/audioplayers/example/lib/tabs/streams.dart index 3e26382de..51b79f559 100644 --- a/packages/audioplayers/example/lib/tabs/streams.dart +++ b/packages/audioplayers/example/lib/tabs/streams.dart @@ -20,6 +20,7 @@ class _StreamsTabState extends State with AutomaticKeepAliveClientMixin { Duration? position, duration; PlayerState? state; + Source? source; late List streams; Duration? streamDuration, streamPosition; @@ -67,6 +68,10 @@ class _StreamsTabState extends State setState(() => state = widget.player.state); } + Future getSource() async { + setState(() => source = widget.player.source); + } + @override Widget build(BuildContext context) { super.build(context); @@ -114,6 +119,20 @@ class _StreamsTabState extends State ), ], ), + Row( + children: [ + Btn( + key: const Key('getSource'), + txt: 'Get Source', + onPressed: getSource, + ), + const Pad(width: 8.0), + Text( + source?.toString() ?? '-', + key: const Key('sourceText'), + ), + ], + ), const Divider(color: Colors.black), const Text('Streams'), Text( diff --git a/packages/audioplayers/lib/src/audioplayer.dart b/packages/audioplayers/lib/src/audioplayer.dart index c63db8350..6a2bf41fe 100644 --- a/packages/audioplayers/lib/src/audioplayer.dart +++ b/packages/audioplayers/lib/src/audioplayer.dart @@ -1,4 +1,5 @@ import 'dart:async'; + // TODO(gustl22): remove when upgrading min Flutter version to >=3.3.0 // ignore: unnecessary_import import 'dart:typed_data'; @@ -29,6 +30,10 @@ class AudioPlayer { PlayerState get state => _playerState; + Source? _source; + + Source? get source => _source; + set state(PlayerState state) { if (!_playerStateController.isClosed) { _playerStateController.add(state); @@ -94,6 +99,9 @@ class AudioPlayer { AudioPlayer({String? playerId}) : playerId = playerId ?? _uuid.v4() { _onPlayerCompleteStreamSubscription = onPlayerComplete.listen((_) { state = PlayerState.completed; + if (releaseMode == ReleaseMode.release) { + _source = null; + } }); } @@ -164,6 +172,7 @@ class AudioPlayer { Future release() async { await _platform.release(playerId); state = PlayerState.stopped; + _source = null; } /// Moves the cursor to the desired position. @@ -217,6 +226,7 @@ class AudioPlayer { /// The resources will start being fetched or buffered as soon as you call /// this method. Future setSourceUrl(String url) { + _source = UrlSource(url); return _platform.setSourceUrl(playerId, url, isLocal: false); } @@ -225,6 +235,7 @@ class AudioPlayer { /// The resources will start being fetched or buffered as soon as you call /// this method. Future setSourceDeviceFile(String path) { + _source = DeviceFileSource(path); return _platform.setSourceUrl(playerId, path, isLocal: true); } @@ -234,11 +245,13 @@ class AudioPlayer { /// The resources will start being fetched or buffered as soon as you call /// this method. Future setSourceAsset(String path) async { + _source = AssetSource(path); final url = await audioCache.load(path); return _platform.setSourceUrl(playerId, url.path, isLocal: true); } Future setSourceBytes(Uint8List bytes) { + _source = BytesSource(bytes); return _platform.setSourceBytes(playerId, bytes); } @@ -277,6 +290,8 @@ class AudioPlayer { _onPlayerCompleteStreamSubscription.cancel() ]; + _source = null; + await Future.wait(futures); } } diff --git a/packages/audioplayers/test/audioplayers_test.dart b/packages/audioplayers/test/audioplayers_test.dart index 95aa3f9f3..e5ca2a7e1 100644 --- a/packages/audioplayers/test/audioplayers_test.dart +++ b/packages/audioplayers/test/audioplayers_test.dart @@ -27,6 +27,21 @@ void main() { } group('AudioPlayers', () { + test('#setSource', () async { + calls.clear(); + final player = AudioPlayer(); + expect(player.source, null); + + await player.setSource(UrlSource('internet.com/file.mp3')); + expect(popLastCall().method, 'setSourceUrl'); + expect(player.source, isInstanceOf()); + final urlSource = player.source as UrlSource?; + expect(urlSource?.url, 'internet.com/file.mp3'); + + await player.release(); + expect(player.source, null); + }); + test('#play', () async { calls.clear(); final player = AudioPlayer();