Signing, Installer, New Workflows
This commit is contained in:
315
flutter_app/lib/services/window_service.dart
Normal file
315
flutter_app/lib/services/window_service.dart
Normal file
@@ -0,0 +1,315 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user