Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ FlutterRingtonePlayer().playNotification();
There's also this generic method allowing you to specify in detail what kind of ringtone should be played:

```dart
FlutterRingtonePlayer().play(
var _soundId= await FlutterRingtonePlayer().play(
android: AndroidSounds.notification,
ios: IosSounds.glass,
repeatTime: 5, // Ios only
looping: true, // Android only - API >= 28
volume: 0.1, // Android only - API >= 28
asAlarm: false, // Android only - all APIs
Expand All @@ -35,37 +36,39 @@ Also you can specify a custom ringtone from assets, or provide direct path to fi
both Android and iOS:

```dart
FlutterRingtonePlayer().play(fromAsset: "assets/ringtone.wav");
var _soundId= await FlutterRingtonePlayer().play(fromAsset: "assets/ringtone.wav");
```

```dart
FlutterRingtonePlayer().play(fromFile: "assets/ringtone.wav");
var _soundId= await FlutterRingtonePlayer().play(fromFile: "assets/ringtone.wav");
```

You can specify a platform specific ringtone and it will override the one from assets:
```dart
FlutterRingtonePlayer().play(
var _soundId= await FlutterRingtonePlayer().play(
fromAsset: "assets/ringtone.wav", // will be the sound on Android
ios: IosSounds.glass // will be the sound on iOS
);
```

### .play() optional attributes

| Attribute | Description |
| -------------- | ------------ |
| `bool` looping | Enables looping of ringtone. Requires `FlutterRingtonePlayer().stop();` to stop ringing. |
| `double` volume | Sets ringtone volume in range 0 to 1.0. |
| `bool` asAlarm | Allows to ignore device's silent/vibration mode and play given sound anyway. |
| Attribute | Description |
|-------------------|------------------------------------------------------------------------------------------|
| `bool` looping | Enables looping of ringtone. Requires `FlutterRingtonePlayer().stop();` to stop ringing. |
| `double` volume | Sets ringtone volume in range 0 to 1.0. |
| `int` repeatTime | Sets ringtone loops on IOS |
| `bool` asAlarm | Allows to ignore device's silent/vibration mode and play given sound anyway. |


To stop looped ringtone please use:

```dart
FlutterRingtonePlayer().stop();
//pass soundId only for Ios
FlutterRingtonePlayer().stop(soundId: _soundId);
```

Above works only on Android, and please note that by default Alarm & Ringtone sounds are looped.
Please note that by default Alarm & Ringtone sounds are looped.

## Default sounds

Expand All @@ -80,7 +83,7 @@ Above works only on Android, and please note that by default Alarm & Ringtone so
If you want to use any other sound on iOS you can always specify a valid Sound ID and manually construct [IosSound]:

```dart
FlutterRingtonePlayer().play(
var _soundId= await FlutterRingtonePlayer().play(
android: AndroidSounds.notification,
ios: const IosSound(1023),
looping: true,
Expand Down
34 changes: 28 additions & 6 deletions ios/Classes/FlutterRingtonePlayerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,20 @@ + (void)registerWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
[registrar addMethodCallDelegate:instance channel:channel];
}

- (void)playSound:(SystemSoundID)soundId count:(int)count {
AudioServicesPlaySystemSoundWithCompletion(soundId, ^{
// NSLog(@"sound playing done ");
if (count > 1) {
// NSLog(@"sound loop.... %d",count);
[self playSound:soundId count:count - 1];
} else {
return;
}
});
};

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {

if ([@"play" isEqualToString:call.method]) {
SystemSoundID soundId = nil;
CFURLRef soundFileURLRef = nil;
Expand All @@ -28,20 +41,29 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result

// The iosSound overrides fromAsset if exists
if (call.arguments[@"ios"] != nil) {
soundId = (SystemSoundID) [call.arguments[@"ios"] integerValue];
soundId = (SystemSoundID)
[call.arguments[@"ios"] integerValue];
}
if (call.arguments[@"repeatTime"] != nil) {
int repeatTime = [call.arguments[@"repeatTime"] integerValue];
[self playSound:soundId count:repeatTime];
} else {
AudioServicesPlaySystemSound(soundId);
}

AudioServicesPlaySystemSound(soundId);
if (soundFileURLRef != nil) {
CFRelease(soundFileURLRef);
}

result(nil);
NSString *soundIDString = [NSString stringWithFormat:@"%d", soundId];
result(soundIDString);
// result(nil);
} else if ([@"stop" isEqualToString:call.method]) {
SystemSoundID
soundId = (SystemSoundID)
[call.arguments[@"soundId"] integerValue];
AudioServicesDisposeSystemSoundID(soundId);
result(nil);
} else {
result(FlutterMethodNotImplemented);
}
}

@end
14 changes: 8 additions & 6 deletions lib/flutter_ringtone_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,30 @@ export 'ios_sounds.dart';
/// On Android it uses system default sounds for each ringtone type. On iOS it
/// uses some hardcoded values for each type.
class FlutterRingtonePlayer {
Future<void> play({
static Future<dynamic> play({
AndroidSound? android,
IosSound? ios,
String? fromAsset,
String? fromFile,
double? volume,
bool? looping,
int? repeatTime,
bool? asAlarm,
}) {
return FlutterRingtonePlayerPlatform.instance.play(
ios: ios,
volume: volume,
looping: looping,
asAlarm: asAlarm,
repeatTime: repeatTime,
android: android,
fromAsset: fromAsset,
fromFile: fromFile,
);
}

/// Play default alarm sound (looping on Android)
Future<void> playAlarm({
static Future<void> playAlarm({
double? volume,
bool looping = true,
bool asAlarm = true,
Expand All @@ -48,7 +50,7 @@ class FlutterRingtonePlayer {
}

/// Play default notification sound
Future<void> playNotification({
static Future<void> playNotification({
double? volume,
bool? looping,
bool asAlarm = false,
Expand All @@ -58,7 +60,7 @@ class FlutterRingtonePlayer {
}

/// Play default system ringtone (looping on Android)
Future<void> playRingtone({
static Future<void> playRingtone({
double? volume,
bool looping = true,
bool asAlarm = false,
Expand All @@ -69,7 +71,7 @@ class FlutterRingtonePlayer {

/// Stop looping sounds like alarms & ringtones on Android.
/// This is no-op on iOS.
Future<void> stop() {
return FlutterRingtonePlayerPlatform.instance.stop();
static Future<void> stop({String? soundId}) {
return FlutterRingtonePlayerPlatform.instance.stop(soundId);
}
}
13 changes: 8 additions & 5 deletions lib/flutter_ringtone_player_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ class MethodChannelFlutterRingtonePlayer extends FlutterRingtonePlayerPlatform {
/// * [AndroidSounds]
/// * [IosSounds]
@override
Future<void> play({
Future<dynamic> play({
AndroidSound? android,
IosSound? ios,
String? fromAsset,
String? fromFile,
double? volume,
int? repeatTime,
bool? looping,
bool? asAlarm,
}) async {
Expand All @@ -54,8 +55,8 @@ class MethodChannelFlutterRingtonePlayer extends FlutterRingtonePlayerPlatform {
if (looping != null) args['looping'] = looping;
if (volume != null) args['volume'] = volume;
if (asAlarm != null) args['asAlarm'] = asAlarm;

_channel.invokeMethod('play', args);
if (repeatTime != null) args['repeatTime'] = repeatTime;
return _channel.invokeMethod('play', args);
} on PlatformException {
// Not handled
}
Expand Down Expand Up @@ -112,9 +113,11 @@ class MethodChannelFlutterRingtonePlayer extends FlutterRingtonePlayerPlatform {
/// Stop looping sounds like alarms & ringtones on Android.
/// This is no-op on iOS.
@override
Future<void> stop() async {
Future<void> stop(String? soundId) async {
try {
_channel.invokeMethod('stop');
var args = <String, dynamic>{};
if (soundId != null) args['soundId'] = soundId;
_channel.invokeMethod('stop',args);
} on PlatformException {
// Not handled
}
Expand Down
5 changes: 3 additions & 2 deletions lib/flutter_ringtone_player_platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ abstract class FlutterRingtonePlayerPlatform extends PlatformInterface {
_instance = instance;
}

Future<void> play({
Future<dynamic> play({
AndroidSound? android,
IosSound? ios,
int? repeatTime,
String? fromAsset,
String? fromFile,
double? volume,
Expand Down Expand Up @@ -67,7 +68,7 @@ abstract class FlutterRingtonePlayerPlatform extends PlatformInterface {

/// Stop looping sounds like alarms & ringtones on Android.
/// This is no-op on iOS.
Future<void> stop() {
Future<void> stop(String? soundId) async{
throw UnimplementedError('stop() has not been implemented.');
}
}
5 changes: 3 additions & 2 deletions lib/ios_sounds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
class IosSound {
final int value;

const IosSound(this.value)
const IosSound(int value)
: assert(value >= 1000),
assert(value <= 2000);
assert(value <= 2000),
value = value;
}

/// Common values for [IosSound] as specified by file name.
Expand Down