Reputation: 1
I am working on an alarm app which is created in flutter. I am using android_alarm_manager_plus, flutter_foreground_task, flutter_local_notifications as the major packages to implement core functionality of alarm. I am basically building alarm app to trigger alarm for the selected days on specific time.
The app is able to fire alarms by showing notification & play alarm-audio ,when the app is alive in foreground. But when i terminate my app, after setting alarm; it would show the notification. But no alarm-audio is played.
here is my pubspec.yaml packages
When the alarm is being fired, the "alarmCallback" is executed. Inside it I have tried to start the foreground service that plays the audio in loop. Whenever the user taps on "stop alarm" notification button, i am stopping the audio and closing the foreground service.
I tried to google about this problem, not much help exists there. So please help me to resolve this issue. Here is the code of "alarmCallback", "foregroundservice" and "foregroundServiceHandler".
AlarmCallback & foregroundStartServiceCallback code (top level function)
@pragma('vm:entry-point')
void startForegroundTaskCallback() {
FlutterForegroundTask.setTaskHandler(ForegroundServiceHandler());
}
@pragma('vm:entry-point')
void alarmCallback(int id, Map<String, dynamic> params) async {
debugPrint("The alarm callback is running !");
bool isRunningService = await FlutterForegroundTask.isRunningService;
debugPrint("The foreground service is running : $isRunningService");
if (!isRunningService) {
debugPrint("Init foreground task");
ForegroundTaskService.init();
} else {
debugPrint("restart foreground task");
await FlutterForegroundTask.restartService();
}
try {
var map = params['params'] as Map<String, dynamic>;
var alarmModel = Alarmsmodel.fromMap(map);
debugPrint(alarmModel.toString());
assert(alarmModel.audioPath != null,
" Cannot play audio because ,audio path is null !");
ForegroundTaskService.saveData("audioPath", alarmModel.audioPath);
ServiceRequestResult serviceResult =
await ForegroundTaskService.startService();
if (serviceResult is ServiceRequestSuccess) {
debugPrint("service result is success !");
} else if (serviceResult is ServiceRequestFailure) {
debugPrint("service result is faliure !");
}
var weekDay = DateTime.now().weekday;
// logic to reschedule alarm in repetition pattern.
// await LocalAlarmService.rescheduleAlarm(alarmModel, weekDay);
} catch (e) {
debugPrint("Error : $e");
throw Exception("Something went wrong, after triggering alarmCallback");
}
}
This is ForegroundService class that manages the foreground task in flutter
@pragma('vm:entry-point')
void startForegroundTaskCallback() {
FlutterForegroundTask.setTaskHandler(ForegroundServiceHandler());
}
@pragma('vm:entry-point')
void alarmCallback(int id, Map<String, dynamic> params) async {
debugPrint("The alarm callback is running !");
bool isRunningService = await FlutterForegroundTask.isRunningService;
debugPrint("The foreground service is running : $isRunningService");
if (!isRunningService) {
debugPrint("Init foreground task");
ForegroundTaskService.init();
} else {
debugPrint("restart foreground task");
await FlutterForegroundTask.restartService();
}
try {
var map = params['params'] as Map<String, dynamic>;
var alarmModel = Alarmsmodel.fromMap(map);
debugPrint(alarmModel.toString());
assert(alarmModel.audioPath != null,
" Cannot play audio because ,audio path is null !");
ForegroundTaskService.saveData("audioPath", alarmModel.audioPath);
ServiceRequestResult serviceResult =
await ForegroundTaskService.startService();
if (serviceResult is ServiceRequestSuccess) {
debugPrint("service result is success !");
} else if (serviceResult is ServiceRequestFailure) {
debugPrint("service result is faliure !");
}
var weekDay = DateTime.now().weekday;
// logic to reschedule alarm in repetition pattern.
// await LocalAlarmService.rescheduleAlarm(alarmModel, weekDay);
} catch (e) {
debugPrint("Error : $e");
throw Exception("Something went wrong, after triggering alarmCallback");
}
}
Here is the code of foregroundServiceHandler
class ForegroundServiceHandler extends TaskHandler {
@override
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {
debugPrint("Fetching audio path.");
String? audioPath =
await FlutterForegroundTask.getData<String>(key: 'audioPath');
debugPrint("The audio path is $audioPath");
if (audioPath != null && audioPath.isNotEmpty) {
debugPrint(
"The audio path is not null neither its empty . Audiopath is $audioPath");
AudioService.instance.playAudioInForeground(audioPath);
} else {
debugPrint(
"Error : audioPath is missing. (ForegroundServiceHandler)(onStart)");
}
}
@override
void onNotificationButtonPressed(String id) {
super.onNotificationButtonPressed(id);
debugPrint("onNotificationButtonPressed");
if (id == "stop_audio") {
AudioService.instance.stopAudio();
}
FlutterForegroundTask.stopService();
}
@override
void onNotificationPressed() {
super.onNotificationPressed();
debugPrint("onNotificationPressed");
}
@override
void onNotificationDismissed() {
super.onNotificationDismissed();
debugPrint("onNotificationDismissed");
FlutterForegroundTask.stopService();
}
@override
void onReceiveData(Object data) {
super.onReceiveData(data);
debugPrint("onReceiveData");
}
@override
void onRepeatEvent(DateTime timestamp) {}
@override
Future<void> onDestroy(DateTime timestamp) async {
debugPrint("onDestroy invoked. Stopping service");
}
}
This is my code Audio Service
class AudioService {
AudioPlayer? _audioPlayer;
bool isAudioPlaying = false;
static final AudioService instance = AudioService._internal();
factory AudioService() {
return instance;
}
AudioService._internal() {
_audioPlayer = AudioPlayer();
_audioPlayer?.setReleaseMode(ReleaseMode.loop);
_audioPlayer?.onPlayerStateChanged.listen((PlayerState state) {
isAudioPlaying = (state == PlayerState.playing);
});
}
void disposeAudio() {
_audioPlayer?.dispose();
_audioPlayer = null;
}
void playAudio(String path) async {
if (_audioPlayer == null) {
debugPrint("Audio player instance is null !");
} else {
debugPrint("Audio Player instance is not null !");
}
_audioPlayer ??= AudioPlayer(); //ensure its initialized.
if (isAudioPlaying) {
await _audioPlayer?.stop();
}
debugPrint("Playing audio for audiopath : $path");
await _audioPlayer?.play(
AssetSource(path),
volume: 1,
);
}
void stopAudio() async {
_audioPlayer ??= AudioPlayer();
debugPrint("Stopping audio instance hashcode : ${_audioPlayer.hashCode}");
await _audioPlayer!.stop();
}
void playAudioInForeground(String path) async {
String localPath = await getLocalFilePath(path);
debugPrint("Setting audio source url");
debugPrint("playing deviceFilePath .");
await _audioPlayer?.play(DeviceFileSource(localPath), volume: 1);
}
Future<String> getLocalFilePath(String assetPath) async {
// Get the app's document directory.
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/$assetPath';
debugPrint("File path is $filePath");
final file = File(filePath);
bool isFilePresent = await file.exists();
debugPrint("Does file exists $isFilePresent");
if (!file.existsSync()) {
// load assets as bytes.
debugPrint("loading bytes from rootbundle");
ByteData data = await rootBundle.load("assets/$assetPath");
debugPrint("loading bytes from ByteData");
List<int> bytes = data.buffer.asUint8List();
debugPrint("The bytes length is ${bytes.length}");
await file.create(recursive: true);
await file.writeAsBytes(bytes);
debugPrint("written to file");
}
debugPrint("returning device file path - $filePath");
return filePath;
}
}
Upvotes: 0
Views: 43