315 lines
8.2 KiB
Dart
315 lines
8.2 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:window_manager/window_manager.dart';
|
|
import 'package:tray_manager/tray_manager.dart';
|
|
|
|
class WindowService with WindowListener, TrayListener {
|
|
static final WindowService _instance = WindowService._internal();
|
|
factory WindowService() => _instance;
|
|
WindowService._internal();
|
|
|
|
bool _isInitialized = false;
|
|
bool _isMinimizedToTray = false;
|
|
BuildContext? _context;
|
|
|
|
Future<void> initialize({BuildContext? context}) async {
|
|
if (_isInitialized) return;
|
|
|
|
_context = context;
|
|
|
|
// Initialize window manager
|
|
windowManager.addListener(this);
|
|
|
|
// Initialize system tray
|
|
await _initializeTray();
|
|
|
|
_isInitialized = true;
|
|
}
|
|
|
|
Future<void> _initializeTray() async {
|
|
try {
|
|
await trayManager.setIcon(
|
|
kIsWeb ? '' : 'icon.ico',
|
|
isTemplate: false,
|
|
);
|
|
|
|
await trayManager.setToolTip('rmtPocketWatcher - AUEC Price Tracker');
|
|
|
|
// Create tray menu
|
|
Menu menu = Menu(
|
|
items: [
|
|
MenuItem(
|
|
key: 'show_window',
|
|
label: 'Show rmtPocketWatcher',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'check_updates',
|
|
label: 'Check for Updates',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'about',
|
|
label: 'About',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'exit_app',
|
|
label: 'Exit',
|
|
),
|
|
],
|
|
);
|
|
|
|
await trayManager.setContextMenu(menu);
|
|
trayManager.addListener(this);
|
|
|
|
if (kDebugMode) {
|
|
print('System tray initialized successfully');
|
|
}
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print('Failed to initialize system tray: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Window controls
|
|
Future<void> minimizeWindow() async {
|
|
await windowManager.minimize();
|
|
}
|
|
|
|
Future<void> minimizeToTray() async {
|
|
await windowManager.hide();
|
|
_isMinimizedToTray = true;
|
|
|
|
if (kDebugMode) {
|
|
print('Window minimized to system tray');
|
|
}
|
|
}
|
|
|
|
Future<void> showWindow() async {
|
|
await windowManager.show();
|
|
await windowManager.focus();
|
|
_isMinimizedToTray = false;
|
|
|
|
if (kDebugMode) {
|
|
print('Window restored from system tray');
|
|
}
|
|
}
|
|
|
|
Future<void> maximizeWindow() async {
|
|
bool isMaximized = await windowManager.isMaximized();
|
|
if (isMaximized) {
|
|
await windowManager.unmaximize();
|
|
} else {
|
|
await windowManager.maximize();
|
|
}
|
|
}
|
|
|
|
Future<void> closeWindow() async {
|
|
// Close button should exit the app
|
|
await exitApp();
|
|
}
|
|
|
|
Future<void> exitApp() async {
|
|
await trayManager.destroy();
|
|
await windowManager.destroy();
|
|
SystemNavigator.pop();
|
|
}
|
|
|
|
// Window event handlers
|
|
@override
|
|
void onWindowClose() async {
|
|
// Prevent default close behavior
|
|
await closeWindow();
|
|
}
|
|
|
|
@override
|
|
void onWindowMinimize() async {
|
|
// When window is minimized (via minimize button or taskbar), go to tray
|
|
await minimizeToTray();
|
|
if (kDebugMode) {
|
|
print('Window minimized to tray');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onWindowRestore() {
|
|
_isMinimizedToTray = false;
|
|
if (kDebugMode) {
|
|
print('Window restored');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onWindowMaximize() {
|
|
if (kDebugMode) {
|
|
print('Window maximized');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onWindowUnmaximize() {
|
|
if (kDebugMode) {
|
|
print('Window unmaximized');
|
|
}
|
|
}
|
|
|
|
// Tray event handlers
|
|
@override
|
|
void onTrayIconMouseDown() async {
|
|
// Single click to show/hide window
|
|
if (_isMinimizedToTray) {
|
|
await showWindow();
|
|
} else {
|
|
await minimizeToTray();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onTrayIconRightMouseDown() async {
|
|
// Right click shows context menu (handled automatically)
|
|
}
|
|
|
|
@override
|
|
void onTrayMenuItemClick(MenuItem menuItem) async {
|
|
switch (menuItem.key) {
|
|
case 'show_window':
|
|
await showWindow();
|
|
break;
|
|
case 'check_updates':
|
|
await showWindow();
|
|
// The update check will be handled by the UI
|
|
if (kDebugMode) {
|
|
print('Checking for updates from tray menu');
|
|
}
|
|
break;
|
|
case 'about':
|
|
await showWindow();
|
|
_showAboutDialog();
|
|
break;
|
|
case 'exit_app':
|
|
await exitApp();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update tray tooltip with current status
|
|
Future<void> updateTrayTooltip(String status) async {
|
|
try {
|
|
await trayManager.setToolTip('rmtPocketWatcher - $status');
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print('Failed to update tray tooltip: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update tray menu with dynamic content
|
|
Future<void> updateTrayMenu({bool hasUpdate = false}) async {
|
|
try {
|
|
Menu menu = Menu(
|
|
items: [
|
|
MenuItem(
|
|
key: 'show_window',
|
|
label: 'Show rmtPocketWatcher',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'check_updates',
|
|
label: hasUpdate ? '🔄 Update Available!' : 'Check for Updates',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'about',
|
|
label: 'About',
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
key: 'exit_app',
|
|
label: 'Exit',
|
|
),
|
|
],
|
|
);
|
|
|
|
await trayManager.setContextMenu(menu);
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print('Failed to update tray menu: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Getters
|
|
bool get isMinimizedToTray => _isMinimizedToTray;
|
|
bool get isInitialized => _isInitialized;
|
|
|
|
void _showAboutDialog() {
|
|
if (_context != null) {
|
|
// Import the about dialog dynamically to avoid circular imports
|
|
showDialog(
|
|
context: _context!,
|
|
builder: (context) {
|
|
// We'll create a simple about dialog here to avoid import issues
|
|
return AlertDialog(
|
|
backgroundColor: const Color(0xFF1A1F3A),
|
|
title: const Row(
|
|
children: [
|
|
Icon(Icons.analytics, color: Color(0xFF50E3C2), size: 32),
|
|
SizedBox(width: 12),
|
|
Text('rmtPocketWatcher', style: TextStyle(color: Colors.white)),
|
|
],
|
|
),
|
|
content: const Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Star Citizen AUEC Price Tracker',
|
|
style: TextStyle(color: Colors.white70, fontSize: 14),
|
|
),
|
|
SizedBox(height: 8),
|
|
Text(
|
|
'Developed by Lambda Banking Conglomerate',
|
|
style: TextStyle(color: Colors.white70, fontSize: 12),
|
|
),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
'Features:',
|
|
style: TextStyle(
|
|
color: Color(0xFF50E3C2),
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
Text('• Real-time AUEC price tracking', style: TextStyle(color: Colors.white70, fontSize: 12)),
|
|
Text('• Multiple vendor monitoring', style: TextStyle(color: Colors.white70, fontSize: 12)),
|
|
Text('• Historical price charts', style: TextStyle(color: Colors.white70, fontSize: 12)),
|
|
Text('• Price alerts & notifications', style: TextStyle(color: Colors.white70, fontSize: 12)),
|
|
Text('• System tray integration', style: TextStyle(color: Colors.white70, fontSize: 12)),
|
|
],
|
|
),
|
|
actions: [
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF50E3C2),
|
|
foregroundColor: Colors.black,
|
|
),
|
|
child: const Text('Close'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
void dispose() {
|
|
windowManager.removeListener(this);
|
|
trayManager.removeListener(this);
|
|
}
|
|
} |