From 6d55a3cf52f63db6943c2541eca7d296b6b1adb6 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 5 Apr 2022 22:30:39 +0430 Subject: [PATCH] new audio player package basic implementaion --- android/app/src/main/AndroidManifest.xml | 24 +++++++--- ios/Podfile.lock | 28 +++++------ ios/Runner/Info.plist | 4 ++ lib/services/app_initalizer.dart | 2 - lib/services/media/media.dart | 46 ++++++++----------- .../home/direct/widgets/audio_widget.dart | 4 +- .../studio_details/studio_details_state.dart | 6 +-- .../widgets/audio/audio_player_widget.dart | 22 ++++----- .../home/widgets/audio/audio_slider.dart | 11 ++--- lib/views/widgets/didvan/bnb.dart | 15 +++--- pubspec.lock | 42 ++++++----------- pubspec.yaml | 2 +- 12 files changed, 95 insertions(+), 111 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 85b3ee9..9f4f7dd 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ + + + android:windowSoftInputMode="adjustResize" + android:name="com.ryanheise.audioservice.AudioServiceActivity" + android:name="com.yalantis.ucrop.UCropActivity" + android:screenOrientation="portrait" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5e2c2df..f1504a2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - - audio_session (0.0.1): + - assets_audio_player (0.0.1): + - Flutter + - assets_audio_player_web (0.0.1): - Flutter - better_player (0.0.1): - Cache (~> 6.0.0) @@ -83,8 +85,6 @@ PODS: - TOCropViewController (~> 2.6.1) - image_picker (0.0.1): - Flutter - - just_audio (0.0.1): - - Flutter - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) - nanopb/encode (= 2.30908.0) @@ -111,15 +111,14 @@ PODS: - TOCropViewController (2.6.1) - url_launcher_ios (0.0.1): - Flutter - - video_player_avfoundation (0.0.1): - - Flutter - wakelock (0.0.1): - Flutter - webview_flutter_wkwebview (0.0.1): - Flutter DEPENDENCIES: - - audio_session (from `.symlinks/plugins/audio_session/ios`) + - assets_audio_player (from `.symlinks/plugins/assets_audio_player/ios`) + - assets_audio_player_web (from `.symlinks/plugins/assets_audio_player_web/ios`) - better_player (from `.symlinks/plugins/better_player/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) @@ -128,13 +127,11 @@ DEPENDENCIES: - flutter_vibrate (from `.symlinks/plugins/flutter_vibrate/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) - - just_audio (from `.symlinks/plugins/just_audio/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - record (from `.symlinks/plugins/record/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -158,8 +155,10 @@ SPEC REPOS: - TOCropViewController EXTERNAL SOURCES: - audio_session: - :path: ".symlinks/plugins/audio_session/ios" + assets_audio_player: + :path: ".symlinks/plugins/assets_audio_player/ios" + assets_audio_player_web: + :path: ".symlinks/plugins/assets_audio_player_web/ios" better_player: :path: ".symlinks/plugins/better_player/ios" firebase_core: @@ -176,8 +175,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_cropper/ios" image_picker: :path: ".symlinks/plugins/image_picker/ios" - just_audio: - :path: ".symlinks/plugins/just_audio/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" permission_handler_apple: @@ -188,15 +185,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" - video_player_avfoundation: - :path: ".symlinks/plugins/video_player_avfoundation/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" webview_flutter_wkwebview: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - audio_session: 4f3e461722055d21515cf3261b64c973c062f345 + assets_audio_player: edee322b9cb625571b830b35872ead1a295fd917 + assets_audio_player_web: 19826380c44375761aa0b9053665c1e3fbc3b86b better_player: 2406bfe8175203c7a46fa15f9d778d73b12e1646 Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d Firebase: 7e8fe528c161b9271d365217a74c16aaf834578e @@ -216,7 +212,6 @@ SPEC CHECKSUMS: HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 - just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce @@ -227,7 +222,6 @@ SPEC CHECKSUMS: sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de - video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 17f2a3a..d87332a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -28,6 +28,10 @@ Main FirebaseAppDelegateProxyEnabled + UIBackgroundModes + + audio + NSMicrophoneUsageDescription We need to access to the microphone to record audio file NSPhotoLibraryUsageDescription diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index 33c0170..749460e 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -1,5 +1,4 @@ import 'package:didvan/models/settings_data.dart'; -import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -15,7 +14,6 @@ class AppInitializer { StorageService.appDocsDir = (await getApplicationDocumentsDirectory()).path; StorageService.appTempsDir = (await getTemporaryDirectory()).path; - MediaService.init(); } } diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index 5e6d816..e70e2de 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -6,22 +6,15 @@ import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:flutter/foundation.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:just_audio/just_audio.dart'; +import 'package:assets_audio_player/assets_audio_player.dart'; class MediaService { - static final AudioPlayer audioPlayer = AudioPlayer(); + static final audioPlayer = AssetsAudioPlayer(); static String? audioPlayerTag; static StudioDetailsData? currentPodcast; static StudioRequestArgs? podcastPlaylistArgs; - static void init() { - audioPlayer.positionStream.listen((event) { - if (audioPlayer.duration != null && audioPlayer.duration! < event) { - audioPlayer.stop(); - audioPlayer.seek(const Duration(seconds: 0)); - } - }); - } + static Duration? get duration => audioPlayer.current.value?.audio.duration; static Future handleAudioPlayback({ required dynamic audioSource, @@ -37,31 +30,32 @@ class MediaService { isNetworkAudio = false; } if (audioPlayerTag == tag) { - if (audioPlayer.playing) { - await audioPlayer.pause(); - } else { - await audioPlayer.play(); - } - } else { - await audioPlayer.stop(); - audioPlayerTag = tag; - if (isNetworkAudio) { - await audioPlayer.setUrl( + await audioPlayer.playOrPause(); + return; + } + await audioPlayer.stop(); + audioPlayerTag = tag; + if (isNetworkAudio) { + await audioPlayer.open( + Audio.network( isVoiceMessage ? (RequestHelper.baseUrl + audioSource + '?accessToken=${RequestService.token}') : audioSource, + ), + ); + } else { + if (kIsWeb) { + await audioPlayer.open( + Audio.network(audioSource!.replaceAll('%3A', ':')), ); } else { - if (kIsWeb) { - await audioPlayer.setUrl(audioSource!.replaceAll('%3A', ':')); - } else { - await audioPlayer.setFilePath(audioSource); - } + await audioPlayer.open(Audio.file(audioSource)); } - audioPlayer.play(); } + audioPlayer.play(); + audioPlayer.updateCurrentAudioNotification(); } static Future resetAudioPlayer() async { diff --git a/lib/views/home/direct/widgets/audio_widget.dart b/lib/views/home/direct/widgets/audio_widget.dart index 5d99777..5f1c8bd 100644 --- a/lib/views/home/direct/widgets/audio_widget.dart +++ b/lib/views/home/direct/widgets/audio_widget.dart @@ -21,7 +21,7 @@ class AudioWidget extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) { return Row( children: [ @@ -56,7 +56,7 @@ class _AudioControllerButton extends StatelessWidget { @override Widget build(BuildContext context) { return DidvanIconButton( - icon: MediaService.audioPlayer.playing == true && _nowPlaying + icon: MediaService.audioPlayer.isPlaying.value && _nowPlaying ? DidvanIcons.pause_circle_solid : DidvanIcons.play_circle_solid, color: Theme.of(context).colorScheme.focusedBorder, diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 110c93f..14187cf 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -117,12 +117,12 @@ class StudioDetailsState extends CoreProvier { ); if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; - MediaService.audioPlayer.positionStream.listen((event) { + MediaService.audioPlayer.currentPosition.listen((event) { if (MediaService.audioPlayerTag?.contains('message') == true) { return; } - final duration = MediaService.audioPlayer.duration ?? - Duration(seconds: studio.duration); + final duration = + MediaService.duration ?? Duration(seconds: studio.duration); if (event.compareTo(duration) > 0 && nextStudio != null) { if (stopOnPodcastEnds) { MediaService.resetAudioPlayer(); diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 5012e47..78db446 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -107,9 +107,9 @@ class AudioPlayerWidget extends StatelessWidget { onPressed: () { MediaService.audioPlayer.seek( Duration( - seconds: - MediaService.audioPlayer.position.inSeconds + - 30, + seconds: MediaService.audioPlayer.currentPosition + .value.inSeconds + + 30, ), ); }, @@ -126,7 +126,7 @@ class AudioPlayerWidget extends StatelessWidget { Expanded( child: Center( child: StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) { return _PlayPouseAnimatedIcon( audioSource: podcast.link, @@ -149,7 +149,8 @@ class AudioPlayerWidget extends StatelessWidget { Duration( seconds: max( 0, - MediaService.audioPlayer.position.inSeconds - + MediaService.audioPlayer.currentPosition.value + .inSeconds - 10, ), ), @@ -254,16 +255,15 @@ class AudioPlayerWidget extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 64), child: DidvanButton( - style: state.timerValue == - MediaService.audioPlayer.duration?.inMinutes && + style: state.timerValue == MediaService.duration?.inMinutes && state.stopOnPodcastEnds ? ButtonStyleMode.primary : ButtonStyleMode.flat, title: 'پایان پادکست', onPressed: () async { - state.timerValue = - MediaService.audioPlayer.duration!.inMinutes - - MediaService.audioPlayer.position.inMinutes; + state.timerValue = MediaService.duration!.inMinutes - + MediaService + .audioPlayer.currentPosition.value.inMinutes; await controller.animateTo( state.timerValue * 10, duration: DesignConfig.lowAnimationDuration, @@ -337,7 +337,7 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> } void _handleAnimation() { - if (MediaService.audioPlayer.playing) { + if (MediaService.audioPlayer.isPlaying.value) { _animationController.forward(); } else { _animationController.reverse(); diff --git a/lib/views/home/widgets/audio/audio_slider.dart b/lib/views/home/widgets/audio/audio_slider.dart index a65e126..bf8969e 100644 --- a/lib/views/home/widgets/audio/audio_slider.dart +++ b/lib/views/home/widgets/audio/audio_slider.dart @@ -26,7 +26,10 @@ class AudioSlider extends StatelessWidget { child: Directionality( textDirection: TextDirection.ltr, child: StreamBuilder( - stream: _isPlaying ? MediaService.audioPlayer.positionStream : null, + stream: + _isPlaying && MediaService.audioPlayer.currentPosition.hasValue + ? MediaService.audioPlayer.currentPosition + : null, builder: (context, snapshot) => ProgressBar( thumbColor: Theme.of(context).colorScheme.title, progressBarColor: DesignConfig.isDark @@ -34,12 +37,8 @@ class AudioSlider extends StatelessWidget { : Theme.of(context).colorScheme.primary, baseBarColor: Theme.of(context).colorScheme.border, bufferedBarColor: Theme.of(context).colorScheme.splash, - total: MediaService.audioPlayer.duration ?? - Duration(seconds: duration ?? 0), + total: MediaService.duration ?? Duration(seconds: duration ?? 0), progress: snapshot.data ?? Duration.zero, - buffered: _isPlaying - ? MediaService.audioPlayer.bufferedPosition - : Duration.zero, thumbRadius: disableThumb ? 0 : 6, barHeight: 3, timeLabelTextStyle: TextStyle( diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index bcc6682..300ed8f 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -1,3 +1,4 @@ +import 'package:assets_audio_player/assets_audio_player.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; @@ -14,7 +15,6 @@ import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:just_audio/just_audio.dart'; import 'package:provider/provider.dart'; class DidvanBNB extends StatelessWidget { @@ -106,7 +106,7 @@ class _PlayerNavBar extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) => GestureDetector( onTap: () => MediaService.currentPodcast == null ? null @@ -195,12 +195,10 @@ class _PlayerNavBar extends StatelessWidget { ], ), ), - StreamBuilder( - stream: MediaService.audioPlayer.playerStateStream, + StreamBuilder( + stream: MediaService.audioPlayer.onReadyToPlay, builder: (context, snapshot) { - final playerState = MediaService - .audioPlayer.playerState.processingState; - if (playerState == ProcessingState.loading || + if (snapshot.data == null || state.appState == AppState.busy) { return Padding( padding: const EdgeInsets.only( @@ -224,8 +222,7 @@ class _PlayerNavBar extends StatelessWidget { }, ), if (state.appState != AppState.busy && - MediaService.audioPlayer.playerState.processingState != - ProcessingState.loading) + snapshot.data != null) Padding( padding: const EdgeInsets.only( left: 12, diff --git a/pubspec.lock b/pubspec.lock index cbb3c93..f08bc28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + assets_audio_player: + dependency: "direct main" + description: + name: assets_audio_player + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4+1" + assets_audio_player_web: + dependency: transitive + description: + name: assets_audio_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4+1" async: dependency: transitive description: @@ -8,13 +22,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" - audio_session: - dependency: transitive - description: - name: audio_session - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.6+1" audio_video_progress_bar: dependency: "direct main" description: @@ -406,27 +413,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" - just_audio: - dependency: "direct main" - description: - name: just_audio - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.20" - just_audio_platform_interface: - dependency: transitive - description: - name: just_audio_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "4.1.0" - just_audio_web: - dependency: transitive - description: - name: just_audio_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.7" lints: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 63202fa..dfc7dfa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,7 +50,6 @@ dependencies: flutter_vibrate: ^1.3.0 universal_html: ^2.0.8 record: ^3.0.2 - just_audio: ^0.9.18 record_web: ^0.2.1 persian_datetime_picker: ^2.4.0 persian_number_utility: ^1.1.1 @@ -66,6 +65,7 @@ dependencies: expandable_bottom_sheet: ^1.1.1+1 permission_handler: ^9.2.0 better_player: ^0.0.81 + assets_audio_player: ^3.0.4+1 dev_dependencies: