Fix notification sound
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
|||||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
BIN
assets/sounds/MGSSpot.mp3
(Stored with Git LFS)
Normal file
BIN
assets/sounds/MGSSpot.mp3
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/sounds/MeetTheSniper.mp3
(Stored with Git LFS)
Normal file
BIN
assets/sounds/MeetTheSniper.mp3
(Stored with Git LFS)
Normal file
Binary file not shown.
223
lib/main.dart
223
lib/main.dart
@@ -1,5 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io'; // Required for Platform check
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart'; // Needed for LogicalKeyboardKey
|
||||||
import 'package:system_tray/system_tray.dart';
|
import 'package:system_tray/system_tray.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
@@ -7,7 +9,7 @@ import 'package:audioplayers/audioplayers.dart';
|
|||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
const Duration popupInterval = Duration(hours: 1); // How often to pop up
|
const Duration popupInterval = Duration(hours: 1); // How often to pop up
|
||||||
const String notificationSound = 'MeetTheSniper.ogg';
|
const String notificationSound = 'MeetTheSniper.mp3';
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
@@ -141,6 +143,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
|||||||
final TextEditingController _todoController = TextEditingController();
|
final TextEditingController _todoController = TextEditingController();
|
||||||
|
|
||||||
Timer? _popupTimer;
|
Timer? _popupTimer;
|
||||||
|
Timer? _debounceTimer; // Timer for debouncing Todo saves
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -161,6 +164,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
_popupTimer?.cancel();
|
_popupTimer?.cancel();
|
||||||
|
_debounceTimer?.cancel(); // Cancel debounce timer on dispose
|
||||||
_previousEntryController.dispose();
|
_previousEntryController.dispose();
|
||||||
_currentEntryController.dispose();
|
_currentEntryController.dispose();
|
||||||
_todoController.dispose();
|
_todoController.dispose();
|
||||||
@@ -251,11 +255,20 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
|||||||
|
|
||||||
Future<void> _playSound() async {
|
Future<void> _playSound() async {
|
||||||
try {
|
try {
|
||||||
|
// Stop any previous playback before starting anew
|
||||||
|
await _audioPlayer.stop();
|
||||||
// Assumes the sound file is in assets/sounds/
|
// Assumes the sound file is in assets/sounds/
|
||||||
await _audioPlayer.play(AssetSource('sounds/$notificationSound'));
|
await _audioPlayer.play(AssetSource('sounds/$notificationSound'));
|
||||||
debugPrint("Played sound: $notificationSound");
|
debugPrint("Played sound: $notificationSound");
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
debugPrint("Error playing sound: $e");
|
// 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
|
// Handle error, e.g., show a notification or log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,15 +316,19 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
|||||||
|
|
||||||
// --- Add a specific save function for Todo List --- //
|
// --- Add a specific save function for Todo List --- //
|
||||||
void _saveTodoList() {
|
void _saveTodoList() {
|
||||||
// TODO: Implement debounced logic to save the todo list
|
// Cancel any existing timer
|
||||||
// Avoid saving on every single keystroke - use a debounce mechanism
|
if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();
|
||||||
// (e.g., wait 500ms after the last change before saving)
|
|
||||||
String todoList = _todoController.text;
|
// Start a new timer
|
||||||
print("Saving Todo list (placeholder)... [${todoList.length} chars]");
|
_debounceTimer = Timer(const Duration(milliseconds: 500), () {
|
||||||
// --- Your actual todo list persistence logic goes here ---
|
// This code runs after 500ms of inactivity
|
||||||
// Example:
|
String todoList = _todoController.text;
|
||||||
// await saveTodoListToFile(todoList);
|
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
|
// Helper to set initial window config like aspect ratio
|
||||||
@@ -327,94 +344,104 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
|||||||
// --- UI Build --- //
|
// --- UI Build --- //
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
// Listen for keyboard events to close on Escape
|
||||||
appBar: AppBar(
|
return RawKeyboardListener(
|
||||||
title: const Text('Journaler'),
|
focusNode: FocusNode(), // Need a focus node to receive keys
|
||||||
actions: const [
|
autofocus: true, // Ensure it can receive focus
|
||||||
// Remove all buttons
|
onKey: (event) {
|
||||||
// No actions needed anymore
|
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
],
|
debugPrint("Escape key pressed - hiding window.");
|
||||||
),
|
windowManager.hide();
|
||||||
body: Padding(
|
}
|
||||||
padding: const EdgeInsets.all(8.0),
|
},
|
||||||
child: Row(
|
child: Scaffold(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
appBar: AppBar(
|
||||||
children: [
|
title: const Text('Journaler'),
|
||||||
// Main journal area (8-10 columns)
|
actions: const [
|
||||||
Expanded(
|
// Remove all buttons
|
||||||
flex:
|
// No actions needed anymore
|
||||||
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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user