From f1b3892dfcc29b8af6b1a8d6c4f2cd007f797bf9 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Mon, 21 Apr 2025 16:46:48 +0200 Subject: [PATCH] Fix notification sound --- .gitattributes | 1 + assets/sounds/MGSSpot.mp3 | 3 + assets/sounds/MeetTheSniper.mp3 | 3 + lib/main.dart | 223 ++++++++++++++++++-------------- 4 files changed, 132 insertions(+), 98 deletions(-) create mode 100644 assets/sounds/MGSSpot.mp3 create mode 100644 assets/sounds/MeetTheSniper.mp3 diff --git a/.gitattributes b/.gitattributes index 599b827..a4308fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.ico filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text diff --git a/assets/sounds/MGSSpot.mp3 b/assets/sounds/MGSSpot.mp3 new file mode 100644 index 0000000..e58b3bc --- /dev/null +++ b/assets/sounds/MGSSpot.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f49c3b08e3f32c99f4ab7132b6ad3275ef2e8ce9f3371e02b446c0e118bfeed0 +size 8272 diff --git a/assets/sounds/MeetTheSniper.mp3 b/assets/sounds/MeetTheSniper.mp3 new file mode 100644 index 0000000..2d6bcf4 --- /dev/null +++ b/assets/sounds/MeetTheSniper.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:907b385dc78e9603ed9df5b8a05ce422362ace285e254d2d0b61d7fb585d2eae +size 39122 diff --git a/lib/main.dart b/lib/main.dart index ef7004a..69a2a8c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:io'; // Required for Platform check import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; // Needed for LogicalKeyboardKey import 'package:system_tray/system_tray.dart'; import 'package:window_manager/window_manager.dart'; import 'package:audioplayers/audioplayers.dart'; @@ -7,7 +9,7 @@ import 'package:audioplayers/audioplayers.dart'; // --- Configuration --- const Duration popupInterval = Duration(hours: 1); // How often to pop up -const String notificationSound = 'MeetTheSniper.ogg'; +const String notificationSound = 'MeetTheSniper.mp3'; // -------------------- void main() async { @@ -141,6 +143,7 @@ class _MainPageState extends State with WindowListener { final TextEditingController _todoController = TextEditingController(); Timer? _popupTimer; + Timer? _debounceTimer; // Timer for debouncing Todo saves @override void initState() { @@ -161,6 +164,7 @@ class _MainPageState extends State with WindowListener { void dispose() { windowManager.removeListener(this); _popupTimer?.cancel(); + _debounceTimer?.cancel(); // Cancel debounce timer on dispose _previousEntryController.dispose(); _currentEntryController.dispose(); _todoController.dispose(); @@ -251,11 +255,20 @@ class _MainPageState extends State with WindowListener { Future _playSound() async { try { + // Stop any previous playback before starting anew + await _audioPlayer.stop(); // Assumes the sound file is in assets/sounds/ await _audioPlayer.play(AssetSource('sounds/$notificationSound')); debugPrint("Played sound: $notificationSound"); - } catch (e) { - debugPrint("Error playing sound: $e"); + } catch (e, stackTrace) { + // Catch stack trace for more details + debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + debugPrint("Error playing sound '$notificationSound': $e"); + debugPrint("Stack trace: $stackTrace"); + debugPrint( + "Ensure file exists, is valid audio, and assets/sounds/ is in pubspec.yaml", + ); + debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); // Handle error, e.g., show a notification or log } } @@ -303,15 +316,19 @@ class _MainPageState extends State with WindowListener { // --- Add a specific save function for Todo List --- // void _saveTodoList() { - // TODO: Implement debounced logic to save the todo list - // Avoid saving on every single keystroke - use a debounce mechanism - // (e.g., wait 500ms after the last change before saving) - String todoList = _todoController.text; - print("Saving Todo list (placeholder)... [${todoList.length} chars]"); - // --- Your actual todo list persistence logic goes here --- - // Example: - // await saveTodoListToFile(todoList); - // -------------------------------------------------------- + // Cancel any existing timer + if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel(); + + // Start a new timer + _debounceTimer = Timer(const Duration(milliseconds: 500), () { + // This code runs after 500ms of inactivity + String todoList = _todoController.text; + print("Debounced Save: Saving Todo list... [${todoList.length} chars]"); + // --- Your actual todo list persistence logic goes here --- + // Example: + // await saveTodoListToFile(todoList); + // -------------------------------------------------------- + }); } // Helper to set initial window config like aspect ratio @@ -327,94 +344,104 @@ class _MainPageState extends State with WindowListener { // --- UI Build --- // @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Journaler'), - actions: const [ - // Remove all buttons - // No actions needed anymore - ], - ), - body: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Main journal area (8-10 columns) - Expanded( - flex: - 9, // Adjust flex factor (e.g., 8, 9, 10) for desired width ratio - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Previous Entry (read-only conceptually, but TextField for easy display) - Expanded( - child: TextField( - controller: _previousEntryController, - maxLines: null, // Allows unlimited lines - expands: true, // Fills the available space - readOnly: true, // Make it non-editable - style: - Theme.of( - context, - ).textTheme.bodyMedium, // Apply theme text style - decoration: const InputDecoration( - labelText: 'Previous Entry', - // border: OutlineInputBorder(), // Handled by theme - // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default - ), - ), - ), - const SizedBox(height: 8), // Spacing - // Current Entry - Expanded( - child: TextField( - controller: _currentEntryController, - maxLines: null, - expands: true, - autofocus: true, // Focus here when window appears - style: - Theme.of( - context, - ).textTheme.bodyMedium, // Apply theme text style - decoration: const InputDecoration( - labelText: 'Current Entry (What\'s on your mind?)', - // border: OutlineInputBorder(), // Handled by theme - // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default - ), - onChanged: (text) { - // Optional: Add auto-save logic here if desired - }, - ), - ), - ], - ), - ), - const SizedBox(width: 8), // Spacing between columns - // Todo List area (remaining columns) - Expanded( - flex: 3, // Adjust flex factor (12 - 9 = 3, or 12 - 8 = 4, etc.) - child: TextField( - controller: _todoController, - maxLines: null, - expands: true, - style: - Theme.of( - context, - ).textTheme.bodyMedium, // Apply theme text style - decoration: const InputDecoration( - labelText: 'Todo List', - // border: OutlineInputBorder(), // Handled by theme - // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default - ), - onChanged: (text) { - // Auto-save Todo list changes (consider debouncing) - _saveTodoList(); - }, - ), - ), + // Listen for keyboard events to close on Escape + return RawKeyboardListener( + focusNode: FocusNode(), // Need a focus node to receive keys + autofocus: true, // Ensure it can receive focus + onKey: (event) { + if (event.logicalKey == LogicalKeyboardKey.escape) { + debugPrint("Escape key pressed - hiding window."); + windowManager.hide(); + } + }, + child: Scaffold( + appBar: AppBar( + title: const Text('Journaler'), + actions: const [ + // Remove all buttons + // No actions needed anymore ], ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Main journal area (8-10 columns) + Expanded( + flex: + 9, // Adjust flex factor (e.g., 8, 9, 10) for desired width ratio + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Previous Entry (read-only conceptually, but TextField for easy display) + Expanded( + child: TextField( + controller: _previousEntryController, + maxLines: null, // Allows unlimited lines + expands: true, // Fills the available space + style: + Theme.of( + context, + ).textTheme.bodyMedium, // Apply theme text style + decoration: const InputDecoration( + labelText: 'Previous Entry', + // border: OutlineInputBorder(), // Handled by theme + // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default + ), + ), + ), + const SizedBox(height: 8), // Spacing + // Current Entry + Expanded( + child: TextField( + controller: _currentEntryController, + maxLines: null, + expands: true, + autofocus: true, // Focus here when window appears + style: + Theme.of( + context, + ).textTheme.bodyMedium, // Apply theme text style + decoration: const InputDecoration( + labelText: 'Current Entry (What\'s on your mind?)', + // border: OutlineInputBorder(), // Handled by theme + // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default + ), + onChanged: (text) { + // Optional: Add auto-save logic here if desired + }, + ), + ), + ], + ), + ), + const SizedBox(width: 8), // Spacing between columns + // Todo List area (remaining columns) + Expanded( + flex: 3, // Adjust flex factor (12 - 9 = 3, or 12 - 8 = 4, etc.) + child: TextField( + controller: _todoController, + maxLines: null, + expands: true, + style: + Theme.of( + context, + ).textTheme.bodyMedium, // Apply theme text style + decoration: const InputDecoration( + labelText: 'Todo List', + // border: OutlineInputBorder(), // Handled by theme + // contentPadding: EdgeInsets.all(8.0), // Handled by theme or default + ), + onChanged: (text) { + // Auto-save Todo list changes (consider debouncing) + _saveTodoList(); + }, + ), + ), + ], + ), + ), ), ); }