diff --git a/debian/control b/debian/control index 83d5210fd7..60ebdf6d35 100644 --- a/debian/control +++ b/debian/control @@ -4,6 +4,7 @@ Build-Depends: build-essential, curl, google-mock, libapparmor-dev, + libnotify-dev, libvirt-dev, libsystemd-dev, pkg-config, diff --git a/src/client/gui/lib/main.dart b/src/client/gui/lib/main.dart index 1136a7712e..4ddc6f778a 100644 --- a/src/client/gui/lib/main.dart +++ b/src/client/gui/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hotkey_manager/hotkey_manager.dart'; +import 'package:local_notifier/local_notifier.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; import 'package:window_size/window_size.dart'; @@ -24,6 +25,11 @@ void main() async { await setupLogger(); + await localNotifier.setup( + appName: 'Multipass', + shortcutPolicy: ShortcutPolicy.requireCreate, // Only for Windows + ); + // Get the current screen size final screenSize = await getCurrentScreen().then((screen) { return screen?.frame.size; diff --git a/src/client/gui/lib/platform/linux.dart b/src/client/gui/lib/platform/linux.dart index c0238f0565..d17d886b4a 100644 --- a/src/client/gui/lib/platform/linux.dart +++ b/src/client/gui/lib/platform/linux.dart @@ -20,6 +20,9 @@ class LinuxPlatform extends MpPlatform { @override String get ffiLibraryName => 'libdart_ffi.so'; + @override + bool get showLocalUpdateNotifications => false; + @override bool get showToggleWindow => true; diff --git a/src/client/gui/lib/platform/macos.dart b/src/client/gui/lib/platform/macos.dart index 31b9af9e0d..0190c7795a 100644 --- a/src/client/gui/lib/platform/macos.dart +++ b/src/client/gui/lib/platform/macos.dart @@ -19,6 +19,9 @@ class MacOSPlatform extends MpPlatform { @override String get ffiLibraryName => 'libdart_ffi.dylib'; + @override + bool get showLocalUpdateNotifications => true; + @override bool get showToggleWindow => false; diff --git a/src/client/gui/lib/platform/platform.dart b/src/client/gui/lib/platform/platform.dart index 81ff8ff608..befa034100 100644 --- a/src/client/gui/lib/platform/platform.dart +++ b/src/client/gui/lib/platform/platform.dart @@ -18,6 +18,8 @@ abstract class MpPlatform { Map get terminalShortcuts; + bool get showLocalUpdateNotifications; + bool get showToggleWindow; String get altKey => 'Alt'; diff --git a/src/client/gui/lib/platform/windows.dart b/src/client/gui/lib/platform/windows.dart index eaea1a00e0..34fdf230c4 100644 --- a/src/client/gui/lib/platform/windows.dart +++ b/src/client/gui/lib/platform/windows.dart @@ -22,6 +22,9 @@ class WindowsPlatform extends MpPlatform { @override String get ffiLibraryName => 'dart_ffi.dll'; + @override + bool get showLocalUpdateNotifications => true; + @override bool get showToggleWindow => true; diff --git a/src/client/gui/lib/update_available.dart b/src/client/gui/lib/update_available.dart index 3710dc6515..0dd7470196 100644 --- a/src/client/gui/lib/update_available.dart +++ b/src/client/gui/lib/update_available.dart @@ -1,5 +1,6 @@ import 'package:basics/basics.dart'; import 'package:flutter/material.dart'; +import 'package:local_notifier/local_notifier.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -7,6 +8,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'notifications/notification_entries.dart'; import 'notifications/notifications_list.dart'; import 'notifications/notifications_provider.dart'; +import 'platform/platform.dart'; import 'providers.dart'; class UpdateNotifier extends Notifier { @@ -15,14 +17,38 @@ class UpdateNotifier extends Notifier { void set(UpdateInfo updateInfo) { if (updateInfo.version.isBlank) return; + final updateNotificationExists = ref.read(notificationsProvider).any((n) { return n is UpdateAvailableNotification && n.updateInfo == updateInfo; }); if (updateNotificationExists) return; + + // In-app notification ref .read(notificationsProvider.notifier) .add(UpdateAvailableNotification(updateInfo)); + + // Update the state state = updateInfo; + + // Create and show a local notification + _showLocalNotification(updateInfo); + } + + void _showLocalNotification(UpdateInfo updateInfo) { + if (!mpPlatform.showLocalUpdateNotifications) return; + + final notification = LocalNotification( + title: 'Multipass Update Available', + body: 'Version ${updateInfo.version} is available. Click to upgrade now.', + ); + + notification.onClick = () async { + await launchInstallUrl(); + await notification.close(); + }; + + notification.show(); } @override diff --git a/src/client/gui/linux/flutter/generated_plugin_registrant.cc b/src/client/gui/linux/flutter/generated_plugin_registrant.cc index b17333210e..cc34e5ace6 100644 --- a/src/client/gui/linux/flutter/generated_plugin_registrant.cc +++ b/src/client/gui/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) hotkey_manager_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerLinuxPlugin"); hotkey_manager_linux_plugin_register_with_registrar(hotkey_manager_linux_registrar); + g_autoptr(FlPluginRegistrar) local_notifier_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "LocalNotifierPlugin"); + local_notifier_plugin_register_with_registrar(local_notifier_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/src/client/gui/linux/flutter/generated_plugins.cmake b/src/client/gui/linux/flutter/generated_plugins.cmake index 69f1ae7b1b..2772dcfa79 100644 --- a/src/client/gui/linux/flutter/generated_plugins.cmake +++ b/src/client/gui/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST hotkey_manager_linux + local_notifier screen_retriever tray_menu url_launcher_linux diff --git a/src/client/gui/macos/Flutter/GeneratedPluginRegistrant.swift b/src/client/gui/macos/Flutter/GeneratedPluginRegistrant.swift index 9f79e4c39e..20d2bbd4a5 100644 --- a/src/client/gui/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/src/client/gui/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,7 @@ import FlutterMacOS import Foundation import hotkey_manager_macos +import local_notifier import path_provider_foundation import screen_retriever import shared_preferences_foundation @@ -16,6 +17,7 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin")) + LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/src/client/gui/pubspec.lock b/src/client/gui/pubspec.lock index 5ce4a4a17b..f526d47260 100644 --- a/src/client/gui/pubspec.lock +++ b/src/client/gui/pubspec.lock @@ -301,6 +301,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + local_notifier: + dependency: "direct main" + description: + name: local_notifier + sha256: f6cfc933c6fbc961f4e52b5c880f68e41b2d3cd29aad557cc654fd211093a025 + url: "https://pub.dev" + source: hosted + version: "0.1.6" logger: dependency: "direct main" description: diff --git a/src/client/gui/pubspec.yaml b/src/client/gui/pubspec.yaml index ed9cd16b1a..d6bbf76e62 100644 --- a/src/client/gui/pubspec.yaml +++ b/src/client/gui/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: grpc: ^4.0.1 hotkey_manager: ^0.2.3 intl: ^0.19.0 + local_notifier: ^0.1.6 logger: ^2.4.0 path_provider: ^2.1.4 protobuf: ^3.1.0 diff --git a/src/client/gui/windows/flutter/generated_plugin_registrant.cc b/src/client/gui/windows/flutter/generated_plugin_registrant.cc index 4669077fae..96c74b967c 100644 --- a/src/client/gui/windows/flutter/generated_plugin_registrant.cc +++ b/src/client/gui/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { HotkeyManagerWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi")); + LocalNotifierPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalNotifierPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); TrayMenuPluginCApiRegisterWithRegistrar( diff --git a/src/client/gui/windows/flutter/generated_plugins.cmake b/src/client/gui/windows/flutter/generated_plugins.cmake index fed0f46a5a..237434db82 100644 --- a/src/client/gui/windows/flutter/generated_plugins.cmake +++ b/src/client/gui/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST hotkey_manager_windows + local_notifier screen_retriever tray_menu url_launcher_windows