new audio player package basic implementaion

This commit is contained in:
MohammadTaha Basiri 2022-04-05 22:30:39 +04:30
parent 2836b6f335
commit 6d55a3cf52
12 changed files with 95 additions and 111 deletions

View File

@ -4,6 +4,8 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:label="Didvan"
android:icon="@mipmap/ic_launcher"
@ -12,13 +14,17 @@
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
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">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@ -40,10 +46,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<service android:name="com.ryanheise.audioservice.AudioService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
</application>

View File

@ -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

View File

@ -28,6 +28,10 @@
<string>Main</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>NSMicrophoneUsageDescription</key>
<string>We need to access to the microphone to record audio file</string>
<key>NSPhotoLibraryUsageDescription</key>

View File

@ -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();
}
}

View File

@ -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<void> 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();
await audioPlayer.playOrPause();
return;
}
} else {
await audioPlayer.stop();
audioPlayerTag = tag;
if (isNetworkAudio) {
await audioPlayer.setUrl(
await audioPlayer.open(
Audio.network(
isVoiceMessage
? (RequestHelper.baseUrl +
audioSource +
'?accessToken=${RequestService.token}')
: audioSource,
),
);
} else {
if (kIsWeb) {
await audioPlayer.setUrl(audioSource!.replaceAll('%3A', ':'));
await audioPlayer.open(
Audio.network(audioSource!.replaceAll('%3A', ':')),
);
} else {
await audioPlayer.setFilePath(audioSource);
await audioPlayer.open(Audio.file(audioSource));
}
}
audioPlayer.play();
}
audioPlayer.updateCurrentAudioNotification();
}
static Future<void> resetAudioPlayer() async {

View File

@ -21,7 +21,7 @@ class AudioWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>(
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,

View File

@ -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();

View File

@ -107,8 +107,8 @@ class AudioPlayerWidget extends StatelessWidget {
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds:
MediaService.audioPlayer.position.inSeconds +
seconds: MediaService.audioPlayer.currentPosition
.value.inSeconds +
30,
),
);
@ -126,7 +126,7 @@ class AudioPlayerWidget extends StatelessWidget {
Expanded(
child: Center(
child: StreamBuilder<bool>(
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();

View File

@ -26,7 +26,10 @@ class AudioSlider extends StatelessWidget {
child: Directionality(
textDirection: TextDirection.ltr,
child: StreamBuilder<Duration>(
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(

View File

@ -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<bool>(
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<PlayerState>(
stream: MediaService.audioPlayer.playerStateStream,
StreamBuilder<PlayingAudio?>(
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,

View File

@ -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:

View File

@ -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: