import 'dart:io'; import 'package:audio_service/audio_service.dart'; import 'package:flutter/services.dart'; import 'package:freezer/api/player/audio_handler.dart'; import 'package:freezer/settings.dart'; import 'package:freezer/translations.i18n.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; final sysTray = SysTray._(); /// Handles system tray and window events class SysTray with TrayListener, WindowListener { SysTray._(); static String getIcon({bool forcePng = false}) { if (Platform.isWindows && !forcePng) { if (settings.useColorTrayIcon) { return 'assets/icon.ico'; } return 'assets/icon_mono_small.ico'; } if (settings.useColorTrayIcon) { return 'assets/icon_small.png'; } return 'assets/icon_mono_small.png'; } bool _inited = false; Future init() async { if (_inited) return; _inited = true; windowManager.addListener(this); updateIcon(); try { await trayManager.setToolTip('freezer'); // ignore: empty_catches } catch (e) {} await updateContextMenu(); trayManager.addListener(this); playerHelper.playing .listen((playing) => updateContextMenu(playing: playing)); audioHandler.mediaItem .listen((mediaItem) => updateContextMenu(mediaItem: mediaItem)); } Future updateIcon() { return trayManager.setIcon(getIcon()); } Future updateContextMenu({bool? playing, MediaItem? mediaItem}) async { playing ??= playerHelper.playing.valueOrNull ?? false; mediaItem ??= audioHandler.mediaItem.valueOrNull; // create context menu final menu = Menu(items: [ if (mediaItem != null) ...[ MenuItem(label: mediaItem.title, disabled: true), MenuItem(label: mediaItem.artist!, disabled: true), ], MenuItem.separator(), MenuItem( label: 'Previous'.i18n, onClick: (_) => audioHandler.skipToPrevious()), playing ? MenuItem(label: 'Pause'.i18n, onClick: (_) => audioHandler.pause()) : MenuItem(label: 'Play'.i18n, onClick: (_) => audioHandler.play()), MenuItem(label: 'Next'.i18n, onClick: (_) => audioHandler.skipToNext()), MenuItem.separator(), MenuItem( label: 'Show'.i18n, // we can safely ignore it if it errors, as it's expected onClick: (_) => windowManager.show().catchError((e) {})), MenuItem( label: 'Exit'.i18n, onClick: (_) async { await audioHandler.pause(); SystemNavigator.pop(); }, ), ]); // set context menu await trayManager.setContextMenu(menu); } @override void onTrayIconMouseUp() async { try { await windowManager.show(); // ignore: empty_catches } catch (e) {} } @override void onTrayIconRightMouseUp() => trayManager.popUpContextMenu(); @override void onWindowClose() { // release resources before closing audioHandler.dispose(); } }