diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1852ae..d7a735b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - Fixed [#386](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/pull/386) - On permission denied isRecording flag changed - Fixed [#384](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues/384) - Provide a callback of drag,tap,start and end details on user gesture - Fixed [#309](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues/309) - Added support for Liner PCM codec in iOS +- Fixes [#325](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues/325) - Added feature to pause all player controller at once. +- Fixes [#379](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/pull/379) - Getting error on dispose(Cannot add new events after calling close) ## 1.2.0 diff --git a/README.md b/README.md index 1d671e8f..2a309b9e 100644 --- a/README.md +++ b/README.md @@ -357,6 +357,13 @@ playerController.release(); playerController.stopAllPlayer(); ``` There could be any number of players but you can just call this function from any **one** player and it will stop all the players. + +#### Pausing players all at once +```dart +playerController.pauseAllPlayers(); +``` +There could be any number of players but you can just call this function from any **one** player and it will pause all the players. + #### Disposing the controller ```dart playerController.dispose(); diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/AudioPlayer.kt b/android/src/main/kotlin/com/simform/audio_waveforms/AudioPlayer.kt index 3b937cb7..2f5f76d5 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/AudioPlayer.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/AudioPlayer.kt @@ -129,26 +129,19 @@ class AudioPlayer( } } - fun stop(result: MethodChannel.Result) { + fun stop() { stopListening() if (playerListener != null) { player?.removeListener(playerListener!!) } isPlayerPrepared = false player?.stop() - result.success(true) } - fun pause(result: MethodChannel.Result) { - try { - stopListening() - player?.pause() - result.success(true) - } catch (e: Exception) { - result.error(Constants.LOG_TAG, "Failed to pause the player", e.toString()) - } - + fun pause() { + stopListening() + player?.pause() } fun release(result: MethodChannel.Result) { diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt b/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt index 4e4a1b25..8a2f8315 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt @@ -13,7 +13,6 @@ import androidx.core.app.ActivityCompat import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry import java.io.IOException -import java.lang.IllegalStateException import kotlin.math.log10 private const val LOG_TAG = "AudioWaveforms" @@ -38,13 +37,13 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { } fun initRecorder( - path: String, - result: MethodChannel.Result, - recorder: MediaRecorder?, - encoder: Int, - outputFormat: Int, - sampleRate: Int, - bitRate: Int? + path: String, + result: MethodChannel.Result, + recorder: MediaRecorder?, + encoder: Int, + outputFormat: Int, + sampleRate: Int, + bitRate: Int? ) { recorder?.apply { setAudioSource(MediaRecorder.AudioSource.MIC) @@ -66,7 +65,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { fun stopRecording(result: MethodChannel.Result, recorder: MediaRecorder?, path: String) { try { - val hashMap : HashMap = HashMap() + val hashMap: HashMap = HashMap() try { recorder?.stop() @@ -136,9 +135,9 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { } override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray + requestCode: Int, + permissions: Array, + grantResults: IntArray ): Boolean { return if (requestCode == RECORD_AUDIO_REQUEST_CODE) { successCallback?.onSuccess(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) @@ -150,7 +149,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { private fun isPermissionGranted(activity: Activity?): Boolean { val result = - ActivityCompat.checkSelfPermission(activity!!, permissions[0]) + ActivityCompat.checkSelfPermission(activity!!, permissions[0]) return result == PackageManager.PERMISSION_GRANTED } @@ -159,8 +158,8 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { if (!isPermissionGranted(activity)) { activity?.let { ActivityCompat.requestPermissions( - it, permissions, - RECORD_AUDIO_REQUEST_CODE + it, permissions, + RECORD_AUDIO_REQUEST_CODE ) } } else { @@ -183,6 +182,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { MediaRecorder.AudioEncoder.AAC } } + Constants.vorbis -> return MediaRecorder.AudioEncoder.VORBIS else -> return MediaRecorder.AudioEncoder.AAC @@ -206,6 +206,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { Constants.amr_nb -> return MediaRecorder.OutputFormat.AMR_NB Constants.webm -> return MediaRecorder.OutputFormat.WEBM + Constants.mpeg_2_ts -> { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { MediaRecorder.OutputFormat.MPEG_2_TS @@ -214,6 +215,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { MediaRecorder.OutputFormat.MPEG_4 } } + Constants.aac_adts -> return MediaRecorder.OutputFormat.AAC_ADTS else -> return MediaRecorder.OutputFormat.MPEG_4 } diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/AudioWaveformsPlugin.kt b/android/src/main/kotlin/com/simform/audio_waveforms/AudioWaveformsPlugin.kt index c3811881..e26cfb0b 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/AudioWaveformsPlugin.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/AudioWaveformsPlugin.kt @@ -7,7 +7,6 @@ import android.os.Build import android.util.Log import androidx.annotation.NonNull import androidx.annotation.RequiresApi - import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -18,7 +17,8 @@ import io.flutter.plugin.common.MethodChannel.Result import java.io.File import java.io.IOException import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale /** AudioWaveformsPlugin */ @@ -102,7 +102,8 @@ class AudioWaveformsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { Constants.stopPlayer -> { val key = call.argument(Constants.playerKey) as String? if (key != null) { - audioPlayers[key]?.stop(result) + audioPlayers[key]?.stop() + result.success(true) } else { result.error(Constants.LOG_TAG, "Player key can't be null", "") } @@ -111,7 +112,8 @@ class AudioWaveformsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { Constants.pausePlayer -> { val key = call.argument(Constants.playerKey) as String? if (key != null) { - audioPlayers[key]?.pause(result) + audioPlayers[key]?.pause() + result.success(true) } else { result.error(Constants.LOG_TAG, "Player key can't be null", "") } @@ -187,11 +189,7 @@ class AudioWaveformsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } Constants.stopAllPlayers -> { - for ((key, _) in audioPlayers) { - audioPlayers[key]?.stop(result) - audioPlayers[key] = null - } - result.success(true) + stopAllPlayer(result) } Constants.finishMode -> { @@ -202,6 +200,10 @@ class AudioWaveformsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } } + Constants.pauseAllPlayers -> { + pauseAllPlayer(result) + } + else -> result.notImplemented() } } @@ -322,4 +324,27 @@ class AudioWaveformsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { pluginBinding!!.removeRequestPermissionsResultListener(this.audioRecorder) } } + + private fun stopAllPlayer(result: MethodChannel.Result) { + try { + for ((key, _) in audioPlayers) { + audioPlayers[key]?.stop() + audioPlayers[key] = null + } + result.success(true) + } catch (e: Exception) { + result.error(Constants.LOG_TAG, "Failed to stop player", e.message) + } + } + + private fun pauseAllPlayer(result: MethodChannel.Result) { + try { + for ((key, _) in audioPlayers) { + audioPlayers[key]?.pause() + } + result.success(true) + } catch (e: Exception) { + result.error(Constants.LOG_TAG, "Failed to pause player", e.message) + } + } } diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/Utils.kt b/android/src/main/kotlin/com/simform/audio_waveforms/Utils.kt index 63977db8..d3d02371 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/Utils.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/Utils.kt @@ -69,6 +69,7 @@ object Constants { const val resultFilePath = "resultFilePath" const val resultDuration = "resultDuration" + const val pauseAllPlayers = "pauseAllPlayers" } enum class FinishMode(val value: Int) { diff --git a/example/lib/chat_bubble.dart b/example/lib/chat_bubble.dart index 1f20e4e7..6dfd13f1 100644 --- a/example/lib/chat_bubble.dart +++ b/example/lib/chat_bubble.dart @@ -152,7 +152,7 @@ class _WaveBubbleState extends State { IconButton( onPressed: () async { controller.playerState.isPlaying - ? await controller.pausePlayer() + ? await controller.stopAllPlayers() : await controller.startPlayer(); controller.setFinishMode(finishMode: FinishMode.loop); }, diff --git a/ios/Classes/AudioPlayer.swift b/ios/Classes/AudioPlayer.swift index d9094b8c..f95b1938 100644 --- a/ios/Classes/AudioPlayer.swift +++ b/ios/Classes/AudioPlayer.swift @@ -91,17 +91,15 @@ class AudioPlayer: NSObject, AVAudioPlayerDelegate { } - func pausePlayer(result: @escaping FlutterResult) { + func pausePlayer() { stopListening() player?.pause() - result(true) } - func stopPlayer(result: @escaping FlutterResult) { + func stopPlayer() { stopListening() player?.stop() timer = nil - result(true) } func release(result: @escaping FlutterResult) { diff --git a/ios/Classes/SwiftAudioWaveformsPlugin.swift b/ios/Classes/SwiftAudioWaveformsPlugin.swift index 2b86ff2e..4b0ad386 100644 --- a/ios/Classes/SwiftAudioWaveformsPlugin.swift +++ b/ios/Classes/SwiftAudioWaveformsPlugin.swift @@ -79,7 +79,8 @@ public class SwiftAudioWaveformsPlugin: NSObject, FlutterPlugin { case Constants.pausePlayer: let key = args?[Constants.playerKey] as? String if(key != nil){ - audioPlayers[key!]?.pausePlayer(result: result) + audioPlayers[key!]?.pausePlayer() + result(true) } else { result(FlutterError(code: Constants.audioWaveforms, message: "Can not pause player", details: "Player key is null")) } @@ -87,7 +88,8 @@ public class SwiftAudioWaveformsPlugin: NSObject, FlutterPlugin { case Constants.stopPlayer: let key = args?[Constants.playerKey] as? String if(key != nil){ - audioPlayers[key!]?.stopPlayer(result: result) + audioPlayers[key!]?.stopPlayer() + result(true) } else { result(FlutterError(code: Constants.audioWaveforms, message: "Can not stop player", details: "Player key is null")) } @@ -136,8 +138,8 @@ public class SwiftAudioWaveformsPlugin: NSObject, FlutterPlugin { result(FlutterError(code: Constants.audioWaveforms, message: "Can not get duration", details: "Player key is null")) } case Constants.stopAllPlayers: - for (playerKey,_) in audioPlayers{ - audioPlayers[playerKey]?.stopPlayer(result: result) + for (playerKey,_) in audioPlayers { + audioPlayers[playerKey]?.stopPlayer() audioPlayers[playerKey] = nil } result(true) @@ -150,6 +152,12 @@ public class SwiftAudioWaveformsPlugin: NSObject, FlutterPlugin { } else { result(FlutterError(code: Constants.audioWaveforms, message: "Can not get waveform data", details: "Player key is null")) } + case Constants.pauseAllPlayers: + for(playerKey,_) in audioPlayers { + audioPlayers[playerKey]?.pausePlayer() + } + result(true) + break default: result(FlutterMethodNotImplemented) break diff --git a/ios/Classes/Utils.swift b/ios/Classes/Utils.swift index 96711f4d..f9c4e0b6 100644 --- a/ios/Classes/Utils.swift +++ b/ios/Classes/Utils.swift @@ -66,6 +66,7 @@ struct Constants { static let linearPCMBitDepth = "linearPCMBitDepth"; static let linearPCMIsBigEndian = "linearPCMIsBigEndian"; static let linearPCMIsFloat = "linearPCMIsFloat"; + static let pauseAllPlayers = "pauseAllPlayers" } diff --git a/lib/src/base/audio_waveforms_interface.dart b/lib/src/base/audio_waveforms_interface.dart index 7cccf049..e7d2b81e 100644 --- a/lib/src/base/audio_waveforms_interface.dart +++ b/lib/src/base/audio_waveforms_interface.dart @@ -209,6 +209,11 @@ class AudioWaveformsInterface { return result ?? false; } + Future pauseAllPlayers() async { + var result = await _methodChannel.invokeMethod(Constants.pauseAllPlayers); + return result ?? false; + } + Future setMethodCallHandler() async { _methodChannel.setMethodCallHandler((call) async { switch (call.method) { diff --git a/lib/src/base/constants.dart b/lib/src/base/constants.dart index 81785cbc..7405137d 100644 --- a/lib/src/base/constants.dart +++ b/lib/src/base/constants.dart @@ -39,6 +39,7 @@ class Constants { static const String current = "current"; static const String onCurrentDuration = "onCurrentDuration"; static const String stopAllPlayers = "stopAllPlayers"; + static const String pauseAllPlayers = "pauseAllPlayers"; static const String onDidFinishPlayingAudio = "onDidFinishPlayingAudio"; static const String extractWaveformData = "extractWaveformData"; static const String noOfSamples = "noOfSamples"; diff --git a/lib/src/controllers/player_controller.dart b/lib/src/controllers/player_controller.dart index 25a723e0..3d5c5d73 100644 --- a/lib/src/controllers/player_controller.dart +++ b/lib/src/controllers/player_controller.dart @@ -318,10 +318,32 @@ class PlayerController extends ChangeNotifier { /// /// This method will close the stream and free resources taken by all /// players. This method will not dispose controller. - Future stopAllPlayers() async { + /// + /// Returns true if all player stopped otherwise return false. + Future stopAllPlayers() async { PlatformStreams.instance.dispose(); - await AudioWaveformsInterface.instance.stopAllPlayers(); - PlatformStreams.instance.playerControllerFactory.clear(); + var isAllPlayersStopped = + await AudioWaveformsInterface.instance.stopAllPlayers(); + if (isAllPlayersStopped) { + PlatformStreams.instance.playerControllerFactory + .forEach((playKey, controller) { + controller._setPlayerState(PlayerState.stopped); + }); + } + return isAllPlayersStopped; + } + + /// This function works similar to stopAllPlayer. + Future pauseAllPlayers() async { + var isAllPlayersPaused = + await AudioWaveformsInterface.instance.pauseAllPlayers(); + if (isAllPlayersPaused) { + PlatformStreams.instance.playerControllerFactory + .forEach((playKey, controller) { + controller._setPlayerState(PlayerState.paused); + }); + } + return isAllPlayersPaused; } /// Sets [_shouldRefresh] flag with provided boolean parameter. diff --git a/pubspec.yaml b/pubspec.yaml index 56dde324..6272ea48 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: audio_waveforms description: A Flutter package that allow you to generate waveform while recording audio or from audio file. -version: 1.2.0 +version: 1.3.0 homepage: https://github.com/SimformSolutionsPvtLtd/audio_waveforms issue_tracker: https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues