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
11 changes: 11 additions & 0 deletions lib/alarm/data/alarm_settings_schema.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:clock_app/alarm/types/schedules/weekly_alarm_schedule.dart';
import 'package:clock_app/alarm/widgets/alarm_task_card.dart';
import 'package:clock_app/alarm/widgets/try_alarm_task_button.dart';
import 'package:clock_app/audio/audio_channels.dart';
import 'package:clock_app/audio/screens/record_ringtone_screen.dart';
import 'package:clock_app/audio/screens/ringtones_screen.dart';
import 'package:clock_app/audio/types/ringtone_player.dart';
import 'package:clock_app/common/data/weekdays.dart';
Expand Down Expand Up @@ -167,6 +168,16 @@ SettingGroup alarmSettingsSchema = SettingGroup(
RingtonePlayer.stop();
},
actions: [
MenuAction(
"Record",
(context) async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const RecordRingtoneScreen()),
);
},
Icons.mic,
),
MenuAction(
"Add",
(context) async {
Expand Down
138 changes: 138 additions & 0 deletions lib/audio/screens/record_ringtone_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import 'dart:io';
import 'dart:math';
import 'package:clock_app/audio/types/ringtone_player.dart';
import 'package:clock_app/common/types/file_item.dart';
import 'package:clock_app/common/utils/list_storage.dart';
import 'package:clock_app/common/utils/snackbar.dart';
import 'package:clock_app/navigation/widgets/app_top_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:record/record.dart';

class RecordRingtoneScreen extends StatefulWidget {
const RecordRingtoneScreen({
super.key,
});

@override
State<RecordRingtoneScreen> createState() => _RecordRingtoneScreenState();
}

class _RecordRingtoneScreenState extends State<RecordRingtoneScreen> {
bool recording = false;
DateTime? recordingStart;
late Record recorder;

@override
void initState() {
recorder = Record();
super.initState();
}

@override
void dispose() {
RingtonePlayer.stop();
recorder.dispose();
super.dispose();
}

void _toggleRecord(BuildContext context) {
if (recording) {
_stopRecord(context);
} else {
_beginRecord(context);
}
}

void _stopRecord(BuildContext context) async {
if (!recording || recordingStart == null) return;

try {
final filename = (await recorder.stop())!;
final file = File(filename);
final bytes = await file.readAsBytes();

final ringtoneList = await loadList<FileItem>("ringtones");
final uri = await saveRingtone(path.basename(filename), bytes);
ringtoneList.add(
FileItem("Recording from ${recordingStart.toString()}", uri,
FileItemType.audio),
);
await saveList("ringtones", ringtoneList);
if(context.mounted) {
showSnackBar(context, AppLocalizations.of(context)!.melodyRecorderOnSaved);
}
} catch (ex) {
//this version of `record` is the latest that works
//with sdk level 21, but it has an issue where spurious
//errors can be thrown when stopping the record.
if(context.mounted) {
showSnackBar(context, AppLocalizations.of(context)!.melodyRecorderOnError, error: true);
}
} finally {
setState(() {
recording = false;
});
}
}

void _beginRecord(BuildContext context) async {
if (recording) return;

if (await recorder.hasPermission()) {
setState(() {
recording = true;
recordingStart = DateTime.now();
});

final folderPath = await getTemporaryDirectory();
final time = recordingStart!
.toLocal()
.toIso8601String()
.replaceAll(RegExp(r'[^0-9]'), "-");
final rand = Random().nextInt(255).toRadixString(16);

final filename = path.join(folderPath.path, "Recording-$time-$rand.m4a");

await recorder.start(path: filename);
if(context.mounted) {
showSnackBar(context, AppLocalizations.of(context)!.melodyRecorderOnStart);
}
}
}

@override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
TextTheme textTheme = theme.textTheme;

return Scaffold(
appBar: AppTopBar(
title: AppLocalizations.of(context)!.melodiesSetting,
),
body: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
color: Colors.redAccent,
shape: const CircleBorder(),
child: InkWell(
customBorder: const CircleBorder(),
highlightColor: Colors.red,
splashColor: Colors.red,
onTap: () => _toggleRecord(context),
child: Padding(
padding: const EdgeInsets.all(40.0),
child: Icon(recording ? Icons.stop : Icons.mic,
size: 100)))),
const SizedBox(height: 16),
],
),
),
);
}
}
2 changes: 1 addition & 1 deletion lib/common/widgets/color_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ColorBox extends StatelessWidget {

@override
Widget build(BuildContext context) {
CardTheme cardTheme = Theme.of(context).cardTheme;
CardThemeData cardTheme = Theme.of(context).cardTheme;

return Container(
width: 36.0,
Expand Down
5 changes: 4 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -778,5 +778,8 @@
"backgroundServiceIntervalSettingDescription": "Lower interval will help keep the app alive, at the cost of some battery life",
"custom": "Custom",
"app": "App",
"materialYou": "Material You"
"materialYou": "Material You",
"melodyRecorderOnStart": "Started recording",
"melodyRecorderOnSaved": "Saved recording to melodies",
"melodyRecorderOnError": "Error saving recording. Please retry"
}
67 changes: 0 additions & 67 deletions lib/settings/screens/list_filter_settings_screen.dart

This file was deleted.

Loading