diff --git a/assets/icon/tray-icon.ico b/assets/icon/tray-icon.ico new file mode 100644 index 0000000..cb5fb2a Binary files /dev/null and b/assets/icon/tray-icon.ico differ diff --git a/assets/icon/tray-icon.png b/assets/icon/tray-icon.png new file mode 100644 index 0000000..2cd8bf9 Binary files /dev/null and b/assets/icon/tray-icon.png differ diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index d47cbab..6dfebbf 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -609,5 +609,8 @@ "other": "{} Source Points" }, "aiThinkingProcess": "AI Thinking Process", - "accountSettingsApplied": "Account settings have been applied." + "accountSettingsApplied": "Account settings have been applied.", + "trayMenuShow": "Show Window", + "trayMenuHide": "Hide Window", + "trayMenuExit": "Exit" } diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index e07913b..7f2d643 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -607,5 +607,8 @@ "other": "{} 源点" }, "aiThinkingProcess": "AI 思考过程", - "accountSettingsApplied": "帐号设置已应用。" + "accountSettingsApplied": "帐号设置已应用。", + "trayMenuShow": "显示窗口", + "trayMenuHide": "隐藏窗口", + "trayMenuExit": "退出" } diff --git a/lib/main.dart b/lib/main.dart index 8e74987..fe07ad5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; +import 'dart:ui'; import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:croppy/croppy.dart'; @@ -10,6 +11,7 @@ import 'package:easy_localization_loader/easy_localization_loader.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -43,6 +45,7 @@ import 'package:surface/widgets/dialog.dart'; import 'package:version/version.dart'; import 'package:workmanager/workmanager.dart'; import 'package:in_app_review/in_app_review.dart'; +import 'package:system_tray/system_tray.dart'; @pragma('vm:entry-point') void appBackgroundDispatcher() { @@ -294,9 +297,51 @@ class _AppSplashScreenState extends State<_AppSplashScreen> { await widgetUpdateRandomPost(); } + Future _trayInitialization() async { + if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; + + final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png'; + final SystemTray systemTray = SystemTray(); + + await systemTray.initSystemTray( + title: "", + iconPath: icon, + ); + + await systemTray.setContextMenu([ + MenuItem(label: 'trayMenuShow'.tr(), onClicked: () => appWindow.show()), + MenuItem(label: 'trayMenuHide'.tr(), onClicked: () => appWindow.hide()), + MenuItem( + label: 'trayMenuExit'.tr(), + onClicked: () { + _appLifecycleListener?.dispose(); + SystemChannels.platform.invokeMethod('SystemNavigator.pop'); + }, + ), + ]); + + systemTray.registerSystemTrayEventHandler((eventName) { + if (eventName == "leftMouseDown") { + Platform.isWindows ? appWindow.show() : systemTray.popUpContextMenu(); + } else if (eventName == "rightMouseDown") { + Platform.isWindows ? systemTray.popUpContextMenu() : appWindow.show(); + } + }); + } + + AppLifecycleListener? _appLifecycleListener; + @override void initState() { super.initState(); + + if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) { + _appLifecycleListener = AppLifecycleListener( + onExitRequested: _onExitRequested, + ); + } + + _trayInitialization(); _initialize().then((_) { _postInitialization(); _tryRequestRating(); @@ -304,6 +349,11 @@ class _AppSplashScreenState extends State<_AppSplashScreen> { }); } + Future _onExitRequested() async { + appWindow.hide(); + return AppExitResponse.cancel; + } + @override Widget build(BuildContext context) { final cfg = context.read(); diff --git a/lib/providers/widget.dart b/lib/providers/widget.dart index 2b3fe71..7fc4898 100644 --- a/lib/providers/widget.dart +++ b/lib/providers/widget.dart @@ -47,6 +47,7 @@ class HomeWidgetProvider { } Future widgetUpdateRandomPost() async { + if (kIsWeb || (!Platform.isAndroid && !Platform.isIOS)) return; final snc = await SnNetworkProvider.createOffContextClient(); final resp = await snc.get('/cgi/co/recommendations/shuffle?take=1'); final post = SnPost.fromJson(resp.data['data'][0]); diff --git a/lib/widgets/chat/chat_message_input.dart b/lib/widgets/chat/chat_message_input.dart index cb45423..8da6692 100644 --- a/lib/widgets/chat/chat_message_input.dart +++ b/lib/widgets/chat/chat_message_input.dart @@ -154,7 +154,6 @@ class ChatMessageInputState extends State { void _showEmojiPicker(BuildContext context) { final overlay = Overlay.of(context); - final sticker = context.read(); _overlayEntry = OverlayEntry( builder: (context) => Positioned( bottom: 16 + MediaQuery.of(context).padding.bottom, @@ -363,7 +362,7 @@ class _StickerPicker extends StatelessWidget { final Function? onDismiss; final Function(String)? onInsert; - const _StickerPicker({super.key, this.onDismiss, required this.originalText, this.onInsert}); + const _StickerPicker({this.onDismiss, required this.originalText, this.onInsert}); @override Widget build(BuildContext context) { diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 452e798..eb4b81f 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -41,6 +42,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) pasteboard_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); pasteboard_plugin_register_with_registrar(pasteboard_registrar); + g_autoptr(FlPluginRegistrar) system_tray_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin"); + system_tray_plugin_register_with_registrar(system_tray_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 214dde0..56e525c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST media_kit_libs_linux media_kit_video pasteboard + system_tray url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e55412b..1552fc2 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -29,6 +29,7 @@ import screen_brightness_macos import share_plus import shared_preferences_foundation import sqflite_darwin +import system_tray import url_launcher_macos import video_compress import wakelock_plus @@ -58,6 +59,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index c4c6dd2..869d09d 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -174,6 +174,8 @@ PODS: - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS + - system_tray (0.0.1): + - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - video_compress (0.3.0): @@ -210,6 +212,7 @@ DEPENDENCIES: - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + - system_tray (from `Flutter/ephemeral/.symlinks/plugins/system_tray/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - video_compress (from `Flutter/ephemeral/.symlinks/plugins/video_compress/macos`) - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) @@ -286,6 +289,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin sqflite_darwin: :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + system_tray: + :path: Flutter/ephemeral/.symlinks/plugins/system_tray/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos video_compress: @@ -334,6 +339,7 @@ SPEC CHECKSUMS: share_plus: 1fa619de8392a4398bfaf176d441853922614e89 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + system_tray: e53c972838c69589ff2e77d6d3abfd71332f9e5d url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 diff --git a/pubspec.lock b/pubspec.lock index b35d8fb..769ac83 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -764,10 +764,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: e37f4c69a07b07bb92622ef6b131a53c9aae48f64b176340af9e8e5238718487 + sha256: "46cdcdcd216f15ac04c80e24e814a89ea7143654442c53ba67fec349b4d44565" url: "https://pub.dev" source: hosted - version: "0.7.5" + version: "0.7.6" flutter_native_splash: dependency: "direct dev" description: @@ -1334,18 +1334,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: b15fad91c4d3d1f2b48c053dd41cb82da007c27407dc9ab5f9aa59881d0e39d4 + sha256: c447a3c3e7be4addf129b8f9ab6a4bd5d166b78918223e223b61fddf4d07e254 url: "https://pub.dev" source: hosted - version: "8.1.4" + version: "8.2.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b + sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.0" pasteboard: dependency: "direct main" description: @@ -1927,6 +1927,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.0+3" + system_tray: + dependency: "direct main" + description: + name: system_tray + sha256: "1bcc11bc230033be20d7443c29f65f68d67169715a838a1122f20fbff5dd2d19" + url: "https://pub.dev" + source: hosted + version: "0.1.1" term_glyph: dependency: transitive description: @@ -2059,10 +2067,10 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "7ed22c21d7fdcc88dd6ba7860384af438cd220b251ad65dfc142ab722fabef61" + sha256: a1870d398158844fe5db12441611ed9a2222ff4340258b539eaf3590c1b4bd7e url: "https://pub.dev" source: hosted - version: "1.1.16" + version: "1.1.17" vector_graphics_codec: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 81a3410..977d1ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -118,6 +118,7 @@ dependencies: flutter_inappwebview: ^6.1.5 html: ^0.15.5 xml: ^6.5.0 + system_tray: ^0.1.1 dev_dependencies: flutter_test: @@ -152,6 +153,8 @@ flutter: - assets/icon/icon.png - assets/icon/icon-dark.png - assets/icon/icon-light-radius.png + - assets/icon/tray-icon.ico + - assets/icon/tray-icon.png - assets/translations/ # An image asset can refer to one or more resolution-specific "variants", see diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8bcf9ef..9054380 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -57,6 +58,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SystemTrayPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SystemTrayPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index ac4942c..a12c723 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -19,6 +19,7 @@ list(APPEND FLUTTER_PLUGIN_LIST permission_handler_windows screen_brightness_windows share_plus + system_tray url_launcher_windows )