From 781598f4c0591c32a6233cba178ee469923fd883 Mon Sep 17 00:00:00 2001 From: chlohal Date: Wed, 7 May 2025 21:25:35 +0100 Subject: [PATCH 1/3] fix: Rework to be buildable --- lib/common/widgets/color_box.dart | 2 +- .../screens/list_filter_settings_screen.dart | 67 ------------------- pubspec.lock | 42 ++++++------ pubspec.yaml | 4 +- 4 files changed, 24 insertions(+), 91 deletions(-) delete mode 100644 lib/settings/screens/list_filter_settings_screen.dart diff --git a/lib/common/widgets/color_box.dart b/lib/common/widgets/color_box.dart index 6eae2f7a..82fe0c04 100644 --- a/lib/common/widgets/color_box.dart +++ b/lib/common/widgets/color_box.dart @@ -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, diff --git a/lib/settings/screens/list_filter_settings_screen.dart b/lib/settings/screens/list_filter_settings_screen.dart deleted file mode 100644 index 2d8cfb58..00000000 --- a/lib/settings/screens/list_filter_settings_screen.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:clock_app/common/types/tag.dart'; -import 'package:clock_app/common/widgets/fab.dart'; -import 'package:clock_app/common/widgets/fields/input_bottom_sheet.dart'; -import 'package:clock_app/common/widgets/list/persistent_list_view.dart'; -import 'package:clock_app/navigation/widgets/app_top_bar.dart'; -import 'package:clock_app/settings/widgets/tag_card.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - -class ListFilterSettingsScreen extends StatefulWidget { - const ListFilterSettingsScreen({ - super.key, - }); - - @override - State createState() => - _ListFilterSettingsScreenState(); -} - -class _ListFilterSettingsScreenState extends State { - final _listController = PersistentListController(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppTopBar(title: AppLocalizations.of(context)!.tagsSetting), - body: Stack( - children: [ - Column( - children: [ - Expanded( - child: PersistentListView( - saveTag: 'tags', - listController: _listController, - itemBuilder: (tag) => TagCard( - key: ValueKey(tag), - tag: tag, - onPressDelete: () => _listController.deleteItem(tag), - onPressDuplicate: () => _listController.duplicateItem(tag), - ), - onTapItem: (tag, index) async { - Tag? newTag = await showTagEditor(tag); - if (newTag == null) return; - tag.copyFrom(newTag); - _listController.changeItems((tags) {}); - }, - // onDeleteItem: _handleDeleteTimer, - placeholderText: "No tags created", - reloadOnPop: true, - isSelectable: true, - ), - ), - ], - ), - FAB( - bottomPadding: 8, - onPressed: () async { - Tag? tag = await showTagEditor(); - if (tag == null) return; - _listController.addItem(tag); - }, - ) - ], - ), - ); - } -} diff --git a/pubspec.lock b/pubspec.lock index a3c9c2ad..f9876ee8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -166,10 +166,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" convert: dependency: transitive description: @@ -474,10 +474,10 @@ packages: dependency: "direct main" description: name: flutter_slidable - sha256: "673403d2eeef1f9e8483bd6d8d92aae73b1d8bd71f382bc3930f699c731bc27c" + sha256: a857de7ea701f276fd6a6c4c67ae885b60729a3449e42766bb0e655171042801 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" flutter_system_ringtones: dependency: "direct main" description: @@ -621,18 +621,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -693,18 +693,18 @@ packages: dependency: "direct main" description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: "direct main" description: @@ -1029,7 +1029,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -1058,10 +1058,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -1074,10 +1074,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" synchronized: dependency: transitive description: @@ -1106,10 +1106,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" timer_builder: dependency: "direct main" description: @@ -1226,10 +1226,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.3.0" watcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d9a8519e..8b396f8d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: path_provider: ^2.0.11 path: ^1.8.2 sqflite: ^2.2.2 - flutter_slidable: ^3.1.0 + flutter_slidable: ^3.1.2 flutter_system_ringtones: ^0.0.6 # android_alarm_manager_plus: ^4.0.4 android_alarm_manager_plus: @@ -63,7 +63,7 @@ dependencies: receive_intent: ^0.2.5 watcher: ^1.1.0 dynamic_color: ^1.7.0 - material_color_utilities: ^0.8.0 + material_color_utilities: ^0.11.1 flutter_oss_licenses: ^3.0.2 locale_names: ^1.1.1 # home_widget: ^0.5.0 From 78c8e61c81c43a2257bb00b151cdf1bc8c9b0815 Mon Sep 17 00:00:00 2001 From: chlohal Date: Wed, 21 May 2025 06:15:09 +0100 Subject: [PATCH 2/3] Add a screen to record an impromptu alarm ringtone. This is helpful to wake someone up with something different every time, or to use speech with something specific to their scenario rather than a generic tone (which has been shown to be better at waking someone up without sleep inertia) --- lib/alarm/data/alarm_settings_schema.dart | 11 ++ lib/audio/screens/record_ringtone_screen.dart | 138 ++++++++++++++++++ pubspec.lock | 48 ++++++ pubspec.yaml | 2 + 4 files changed, 199 insertions(+) create mode 100644 lib/audio/screens/record_ringtone_screen.dart diff --git a/lib/alarm/data/alarm_settings_schema.dart b/lib/alarm/data/alarm_settings_schema.dart index ffb055b5..66423d62 100644 --- a/lib/alarm/data/alarm_settings_schema.dart +++ b/lib/alarm/data/alarm_settings_schema.dart @@ -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'; @@ -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 { diff --git a/lib/audio/screens/record_ringtone_screen.dart b/lib/audio/screens/record_ringtone_screen.dart new file mode 100644 index 00000000..6b17bcce --- /dev/null +++ b/lib/audio/screens/record_ringtone_screen.dart @@ -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/common/widgets/fab.dart'; +import 'package:clock_app/common/widgets/file_item_card.dart'; +import 'package:clock_app/common/widgets/list/persistent_list_view.dart'; +import 'package:clock_app/developer/logic/logger.dart'; +import 'package:clock_app/navigation/widgets/app_top_bar.dart'; +import 'package:clock_app/settings/types/setting_item.dart'; +import 'package:clock_app/system/data/device_info.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:path/path.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:path/path.dart' as path; +import 'package:record/record.dart'; + +class RecordRingtoneScreen extends StatefulWidget { + const RecordRingtoneScreen({ + super.key, + }); + + @override + State createState() => _RecordRingtoneScreenState(); +} + +class _RecordRingtoneScreenState extends State { + 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() { + if (recording) { + _stopRecord(); + } else { + _beginRecord(); + } + } + + void _stopRecord() 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("ringtones"); + final uri = await saveRingtone(path.basename(filename), bytes); + ringtoneList.add( + FileItem("Recording from ${recordingStart.toString()}", uri, + FileItemType.audio), + ); + await saveList("ringtones", ringtoneList); + } 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. + } finally { + setState(() { + recording = false; + }); + } + } + + void _beginRecord() 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); + } + } + + @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(), + child: Padding( + padding: const EdgeInsets.all(40.0), + child: Icon(recording ? Icons.stop : Icons.mic, + size: 100)))), + const SizedBox(height: 16), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index f9876ee8..f3c5c9bf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -953,6 +953,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.5" + record: + dependency: "direct main" + description: + name: record + sha256: "7d29f4d8a27caafa6d5c0740aeefb49a73f995b7c385a469d9ef7b3e3764035c" + url: "https://pub.dev" + source: hosted + version: "4.3.3" + record_linux: + dependency: transitive + description: + name: record_linux + sha256: "4e1e22666956c360a3c4b0c32651610fd9591d339fc5771153c549756dfc775d" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: "1b107e05c04ee93846b30b7778f6f0b89a33b994a336eac4f489540226202dc6" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + record_platform_interface: + dependency: transitive + description: + name: record_platform_interface + sha256: "6dcae5252ff7157c5f5b3117014cc4c4e267736042b13b5bf736518cddf401a6" + url: "https://pub.dev" + source: hosted + version: "0.4.0" + record_web: + dependency: transitive + description: + name: record_web + sha256: "2954f67af2c2aef960cbd8c8fbefaeee0244697d9a7d6b15d887e54d9301372b" + url: "https://pub.dev" + source: hosted + version: "0.4.0" + record_windows: + dependency: transitive + description: + name: record_windows + sha256: e0b78b6f336880852257d1fe8d06441d1bdb24524c6a9c3aee562e4a7232152b + url: "https://pub.dev" + source: hosted + version: "0.6.1" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8b396f8d..5442ddcb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -88,6 +88,8 @@ dependencies: mime: ^1.0.6 analog_clock: ^0.1.1 animated_analog_clock: ^0.1.0 + #Latest version that supports current Android SDK version + record: 4.3.3 # animated_reorderable_list: ^1.1.1 # animated_reorderable_list: # path: "../animated_reorderable_list" From 353cadf37a77fb1513e5b22c83cc53e59a7f11aa Mon Sep 17 00:00:00 2001 From: chlohal Date: Wed, 21 May 2025 07:18:47 +0100 Subject: [PATCH 3/3] Add snackbars to notify use about state --- lib/audio/screens/record_ringtone_screen.dart | 30 +++++++++---------- lib/l10n/app_en.arb | 5 +++- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/audio/screens/record_ringtone_screen.dart b/lib/audio/screens/record_ringtone_screen.dart index 6b17bcce..036ffee9 100644 --- a/lib/audio/screens/record_ringtone_screen.dart +++ b/lib/audio/screens/record_ringtone_screen.dart @@ -4,19 +4,10 @@ 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/common/widgets/fab.dart'; -import 'package:clock_app/common/widgets/file_item_card.dart'; -import 'package:clock_app/common/widgets/list/persistent_list_view.dart'; -import 'package:clock_app/developer/logic/logger.dart'; import 'package:clock_app/navigation/widgets/app_top_bar.dart'; -import 'package:clock_app/settings/types/setting_item.dart'; -import 'package:clock_app/system/data/device_info.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:path/path.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; import 'package:path/path.dart' as path; import 'package:record/record.dart'; @@ -47,15 +38,15 @@ class _RecordRingtoneScreenState extends State { super.dispose(); } - void _toggleRecord() { + void _toggleRecord(BuildContext context) { if (recording) { - _stopRecord(); + _stopRecord(context); } else { - _beginRecord(); + _beginRecord(context); } } - void _stopRecord() async { + void _stopRecord(BuildContext context) async { if (!recording || recordingStart == null) return; try { @@ -70,10 +61,16 @@ class _RecordRingtoneScreenState extends State { 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; @@ -81,7 +78,7 @@ class _RecordRingtoneScreenState extends State { } } - void _beginRecord() async { + void _beginRecord(BuildContext context) async { if (recording) return; if (await recorder.hasPermission()) { @@ -100,6 +97,9 @@ class _RecordRingtoneScreenState extends State { final filename = path.join(folderPath.path, "Recording-$time-$rand.m4a"); await recorder.start(path: filename); + if(context.mounted) { + showSnackBar(context, AppLocalizations.of(context)!.melodyRecorderOnStart); + } } } @@ -124,7 +124,7 @@ class _RecordRingtoneScreenState extends State { customBorder: const CircleBorder(), highlightColor: Colors.red, splashColor: Colors.red, - onTap: () => _toggleRecord(), + onTap: () => _toggleRecord(context), child: Padding( padding: const EdgeInsets.all(40.0), child: Icon(recording ? Icons.stop : Icons.mic, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4fd37495..96d06532 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -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" }