|
@@ -0,0 +1,131 @@
|
|
|
|
|
+import 'dart:io';
|
|
|
|
|
+
|
|
|
|
|
+import 'package:flutter/foundation.dart';
|
|
|
|
|
+import 'package:flutter/services.dart';
|
|
|
|
|
+import 'package:shortid/shortid.dart';
|
|
|
|
|
+import 'package:path/path.dart' as path;
|
|
|
|
|
+
|
|
|
|
|
+import '../../constants/assets.dart';
|
|
|
|
|
+import 'menu_base/menu_base.dart';
|
|
|
|
|
+import 'tray_listener.dart';
|
|
|
|
|
+
|
|
|
|
|
+const kEventOnTrayIconLButtonDown = 'onTrayIconLButtonDown';
|
|
|
|
|
+const kEventOnTrayIconLButtonUp = 'onTrayIconLButtonUp';
|
|
|
|
|
+const kEventOnTrayIconRButtonDown = 'onTrayIconRButtonDown';
|
|
|
|
|
+const kEventOnTrayIconRButtonUp = 'onTrayIconRButtonUp';
|
|
|
|
|
+const kEventOnTrayMenuItemClick = 'onTrayMenuItemClick';
|
|
|
|
|
+
|
|
|
|
|
+class TrayController {
|
|
|
|
|
+ final _channel = const MethodChannel("app.nomo/tray");
|
|
|
|
|
+ final ObserverList<TrayListener> _listeners = ObserverList<TrayListener>();
|
|
|
|
|
+ Menu? _trayMenu;
|
|
|
|
|
+
|
|
|
|
|
+ TrayController() {
|
|
|
|
|
+ _channel.setMethodCallHandler(_methodCallHandler);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Future<void> _methodCallHandler(MethodCall call) async {
|
|
|
|
|
+ for (final TrayListener listener in _listeners) {
|
|
|
|
|
+ switch (call.method) {
|
|
|
|
|
+ case kEventOnTrayIconLButtonDown:
|
|
|
|
|
+ listener.onTrayIconLButtonDown();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case kEventOnTrayIconLButtonUp:
|
|
|
|
|
+ listener.onTrayIconLButtonUp();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case kEventOnTrayIconRButtonDown:
|
|
|
|
|
+ listener.onTrayIconRButtonDown();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case kEventOnTrayIconRButtonUp:
|
|
|
|
|
+ listener.onTrayIconRButtonUp();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case kEventOnTrayMenuItemClick:
|
|
|
|
|
+ int id = call.arguments['id'];
|
|
|
|
|
+ MenuItem? menuItem = _trayMenu?.getMenuItemById(id);
|
|
|
|
|
+ if (menuItem != null) {
|
|
|
|
|
+ bool? oldChecked = menuItem.checked;
|
|
|
|
|
+ if (menuItem.onClick != null) {
|
|
|
|
|
+ menuItem.onClick!(menuItem);
|
|
|
|
|
+ }
|
|
|
|
|
+ listener.onTrayMenuItemClick(menuItem);
|
|
|
|
|
+
|
|
|
|
|
+ bool? newChecked = menuItem.checked;
|
|
|
|
|
+ if (oldChecked != newChecked) {
|
|
|
|
|
+ await _setContextMenu(_trayMenu!);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Future<void> _setContextMenu(Menu menu) {
|
|
|
|
|
+ _trayMenu = menu;
|
|
|
|
|
+ final Map<String, dynamic> arguments = {'menu': menu.toJson()};
|
|
|
|
|
+ return _channel.invokeMethod('setContextMenu', arguments);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Future<void> _setSystemTrayWindows(String iconPath, String? tooltip) {
|
|
|
|
|
+ final Map<String, dynamic> arguments = {
|
|
|
|
|
+ 'id': shortid.generate(),
|
|
|
|
|
+ 'icon': path.joinAll([
|
|
|
|
|
+ path.dirname(Platform.resolvedExecutable),
|
|
|
|
|
+ 'data/flutter_assets',
|
|
|
|
|
+ iconPath,
|
|
|
|
|
+ ]),
|
|
|
|
|
+ 'tooltip': tooltip ?? '',
|
|
|
|
|
+ };
|
|
|
|
|
+ return _channel.invokeMethod('setSystemTray', arguments);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// 设置托盘图标
|
|
|
|
|
+ Future<void> setSystemTray(
|
|
|
|
|
+ bool isConnected,
|
|
|
|
|
+ bool isDark, {
|
|
|
|
|
+ String? title,
|
|
|
|
|
+ String? tooltip,
|
|
|
|
|
+ }) {
|
|
|
|
|
+ String iconPath;
|
|
|
|
|
+ if (isConnected) {
|
|
|
|
|
+ iconPath = isDark
|
|
|
|
|
+ ? Assets.trayIconDarkConnectedWin
|
|
|
|
|
+ : Assets.trayIconLightConnectedWin;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ iconPath = isDark
|
|
|
|
|
+ ? Assets.trayIconDarkUnConnectedWin
|
|
|
|
|
+ : Assets.trayIconLightUnConnectedWin;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Platform.isWindows) {
|
|
|
|
|
+ return _setSystemTrayWindows(iconPath, tooltip);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw UnimplementedError();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// 设置托盘菜单
|
|
|
|
|
+ Future<void> setContextMenu(Menu trayMenu) {
|
|
|
|
|
+ return _setContextMenu(trayMenu);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// 弹出系统托盘菜单
|
|
|
|
|
+ Future<void> popUpContextMenu() {
|
|
|
|
|
+ return _channel.invokeMethod('popupContextMenu');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// 销毁系统托盘
|
|
|
|
|
+ Future<void> resetSystemTray() {
|
|
|
|
|
+ return _channel.invokeMethod('resetSystemTray');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool get hasListeners {
|
|
|
|
|
+ return _listeners.isNotEmpty;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void addListener(TrayListener listener) {
|
|
|
|
|
+ _listeners.add(listener);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void removeListener(TrayListener listener) {
|
|
|
|
|
+ _listeners.remove(listener);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|