diff --git a/packages/audioplayers/example/assets/two_beeps.mp2 b/packages/audioplayers/example/assets/two_beeps.mp2 new file mode 100644 index 000000000..6fc543795 Binary files /dev/null and b/packages/audioplayers/example/assets/two_beeps.mp2 differ diff --git a/packages/audioplayers/example/integration_test/lib_test.dart b/packages/audioplayers/example/integration_test/lib_test.dart index e5bed6738..245f43610 100644 --- a/packages/audioplayers/example/integration_test/lib_test.dart +++ b/packages/audioplayers/example/integration_test/lib_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:audioplayers/audioplayers.dart'; @@ -200,4 +201,31 @@ void main() { skip: !features.hasForceSpeaker || !features.hasLowLatency, ); }); + + group('Platform testing', () { + /// This section is to test issues for specific platforms. + /// This can be used to set up scenarios which are hard to implement or + /// reproduce on the native side. + /// Nether the less: common native unit tests are preferred. + testWidgets( + 'test #1260 - mp2', + (WidgetTester tester) async { + final completer = Completer(); + final player = AudioPlayer(); + + player.onPlayerComplete.listen( + (_) => completer.complete(), + onError: completer.completeError, + ); + // The play event is handled asynchronously and therefore the error is + // not thrown while preparing the source, but returned via error stream. + await player.play(AssetSource(mp2Asset)); + await completer.future; + // TODO(gustl22): check error via stream, if supported via #1352 + // Unexpected platform error: MediaPlayer error with + // what:MEDIA_ERROR_UNKNOWN {what:1} extra:MEDIA_ERROR_SYSTEM + }, + skip: kIsWeb || !Platform.isAndroid, + ); + }); } diff --git a/packages/audioplayers/example/lib/tabs/sources.dart b/packages/audioplayers/example/lib/tabs/sources.dart index 5bbc7e355..191bdf8b4 100644 --- a/packages/audioplayers/example/lib/tabs/sources.dart +++ b/packages/audioplayers/example/lib/tabs/sources.dart @@ -24,6 +24,7 @@ const mpgaStreamUrl = 'https://timesradio.wireless.radio/stream'; const asset1 = 'laser.wav'; const asset2 = 'nasa_on_a_mission.mp3'; +const mp2Asset = 'two_beeps.mp2'; class SourcesTab extends StatefulWidget { final AudioPlayer player; diff --git a/packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt b/packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt index 9018e2d79..2ba0cecf0 100644 --- a/packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt +++ b/packages/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt @@ -247,22 +247,47 @@ class WrappedPlayer internal constructor( * Player callbacks */ fun onPrepared() { - prepared = true - ref.handleDuration(this) - if (playing) { - player?.start() - ref.handleIsPlaying() - } - if (shouldSeekTo >= 0 && player?.isLiveStream() != true) { - player?.seekTo(shouldSeekTo) + try { + prepared = true + ref.handleDuration(this) + if (playing) { + player?.start() + ref.handleIsPlaying() + } + if (shouldSeekTo >= 0 && player?.isLiveStream() != true) { + player?.seekTo(shouldSeekTo) + } + } catch (e: Exception) { + handleError(e) } } fun onCompletion() { - if (releaseMode != ReleaseMode.LOOP) { - stop() + try { + if (releaseMode != ReleaseMode.LOOP) { + stop() + } + ref.handleComplete(this) + } catch (e: Exception) { + handleError(e) } - ref.handleComplete(this) + } + + @Suppress("UNUSED_PARAMETER") + fun onBuffering(percent: Int) { + // TODO(luan): expose this as a stream + } + + fun onSeekComplete() { + try { + ref.handleSeekComplete(this) + } catch (e: Exception) { + handleError(e) + } + } + + private fun handleError(error: Throwable) { + ref.handleError(this, error.stackTraceToString()) } fun onError(what: Int, extra: Int): Boolean { @@ -283,15 +308,6 @@ class WrappedPlayer internal constructor( return false } - @Suppress("UNUSED_PARAMETER") - fun onBuffering(percent: Int) { - // TODO(luan): expose this as a stream - } - - fun onSeekComplete() { - ref.handleSeekComplete(this) - } - /** * Internal logic. Private methods */