diff --git a/ui/flutter/android/app/build.gradle b/ui/flutter/android/app/build.gradle index 23ebd8d2..d5bc7f41 100644 --- a/ui/flutter/android/app/build.gradle +++ b/ui/flutter/android/app/build.gradle @@ -28,6 +28,7 @@ android { ndkVersion flutter.ndkVersion compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } @@ -83,4 +84,5 @@ flutter { dependencies { implementation(name: 'libgopeed', ext: 'aar') + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' } diff --git a/ui/flutter/lib/api/model/downloader_config.dart b/ui/flutter/lib/api/model/downloader_config.dart index 323d4c19..d481f89c 100644 --- a/ui/flutter/lib/api/model/downloader_config.dart +++ b/ui/flutter/lib/api/model/downloader_config.dart @@ -89,6 +89,7 @@ class ExtraConfig { bool defaultBtClient; bool notifyWhenNewVersion; bool autoStartTasks; + bool desktopNotification; List downloadCategories; ExtraConfigBt bt = ExtraConfigBt(); @@ -102,6 +103,7 @@ class ExtraConfig { this.defaultBtClient = true, this.notifyWhenNewVersion = true, this.autoStartTasks = false, + this.desktopNotification = true, this.downloadCategories = const [], }); diff --git a/ui/flutter/lib/api/model/downloader_config.g.dart b/ui/flutter/lib/api/model/downloader_config.g.dart index 6c49b2db..01c9aa43 100644 --- a/ui/flutter/lib/api/model/downloader_config.g.dart +++ b/ui/flutter/lib/api/model/downloader_config.g.dart @@ -19,8 +19,7 @@ DownloaderConfig _$DownloaderConfigFromJson(Map json) => ..proxy = ProxyConfig.fromJson(json['proxy'] as Map) ..webhook = WebhookConfig.fromJson(json['webhook'] as Map?) - ..script = - ScriptConfig.fromJson(json['script'] as Map?) + ..script = ScriptConfig.fromJson(json['script'] as Map?) ..autoTorrent = AutoTorrentConfig.fromJson( json['autoTorrent'] as Map?) ..archive = @@ -91,6 +90,7 @@ ExtraConfig _$ExtraConfigFromJson(Map json) => ExtraConfig( defaultBtClient: json['defaultBtClient'] as bool? ?? true, notifyWhenNewVersion: json['notifyWhenNewVersion'] as bool? ?? true, autoStartTasks: json['autoStartTasks'] as bool? ?? false, + desktopNotification: json['desktopNotification'] as bool? ?? true, downloadCategories: (json['downloadCategories'] as List?) ?.map((e) => DownloadCategory.fromJson(e as Map)) .toList() ?? @@ -109,6 +109,7 @@ Map _$ExtraConfigToJson(ExtraConfig instance) => 'defaultBtClient': instance.defaultBtClient, 'notifyWhenNewVersion': instance.notifyWhenNewVersion, 'autoStartTasks': instance.autoStartTasks, + 'desktopNotification': instance.desktopNotification, 'downloadCategories': instance.downloadCategories.map((e) => e.toJson()).toList(), 'bt': instance.bt.toJson(), @@ -156,8 +157,7 @@ Map _$WebhookConfigToJson(WebhookConfig instance) => 'urls': instance.urls, }; -ScriptConfig _$ScriptConfigFromJson(Map json) => - ScriptConfig( +ScriptConfig _$ScriptConfigFromJson(Map json) => ScriptConfig( enable: json['enable'] as bool? ?? false, paths: (json['paths'] as List?)?.map((e) => e as String).toList() ?? diff --git a/ui/flutter/lib/app/modules/app/controllers/app_controller.dart b/ui/flutter/lib/app/modules/app/controllers/app_controller.dart index b93b518d..73de2677 100644 --- a/ui/flutter/lib/app/modules/app/controllers/app_controller.dart +++ b/ui/flutter/lib/app/modules/app/controllers/app_controller.dart @@ -17,6 +17,7 @@ import 'package:uri_to_file/uri_to_file.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:window_manager/window_manager.dart'; + import '../../../../api/api.dart'; import '../../../../api/model/create_task.dart'; import '../../../../api/model/downloader_config.dart'; @@ -38,6 +39,7 @@ import '../../../../util/util.dart'; import '../../../routes/app_pages.dart'; import '../../../rpc/rpc.dart'; import '../../redirect/views/redirect_view.dart'; +import '../../../services/notification_service.dart'; const unixSocketPath = 'gopeed.sock'; @@ -96,6 +98,10 @@ class AppController extends GetxController with WindowListener, TrayListener { _initWindows().onError((error, stackTrace) => logger.w("initWindows error", error, stackTrace)); + + if (Util.isDesktop()) { + Get.put(NotificationService()); + } _initTray().onError( (error, stackTrace) => logger.w("initTray error", error, stackTrace)); diff --git a/ui/flutter/lib/app/modules/setting/views/setting_view.dart b/ui/flutter/lib/app/modules/setting/views/setting_view.dart index d6bcb139..bfc4601d 100644 --- a/ui/flutter/lib/app/modules/setting/views/setting_view.dart +++ b/ui/flutter/lib/app/modules/setting/views/setting_view.dart @@ -498,6 +498,27 @@ class SettingView extends GetView { ); }); + final buildDesktopNotification = !Util.isDesktop() + ? () => null + : _buildConfigItem('desktopNotification', () { + return appController.downloaderConfig.value.extra.desktopNotification + ? 'on'.tr + : 'off'.tr; + }, (Key key) { + return Container( + alignment: Alignment.centerLeft, + child: Switch( + value: appController.downloaderConfig.value.extra.desktopNotification, + onChanged: (bool value) async { + appController.downloaderConfig.update((val) { + val!.extra.desktopNotification = value; + }); + await debounceSave(); + }, + ), + ); + }); + // http config items start final httpConfig = downloaderCfg.value.protocolConfig.http; final buildHttpUa = @@ -1592,6 +1613,7 @@ class SettingView extends GetView { buildBrowserExtension(), buildAutoStartup(), buildMenubarMode(), + buildDesktopNotification(), ]), )), Text('archives'.tr), diff --git a/ui/flutter/lib/app/services/notification_service.dart b/ui/flutter/lib/app/services/notification_service.dart new file mode 100644 index 00000000..d3e752ce --- /dev/null +++ b/ui/flutter/lib/app/services/notification_service.dart @@ -0,0 +1,146 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:get/get.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../../api/api.dart'; +import '../../api/model/task.dart'; +import '../../util/util.dart'; +import '../modules/app/controllers/app_controller.dart'; + +class NotificationService extends GetxService { + Timer? _timer; + final Map _previousStatus = {}; + + final AppController appController = Get.find(); + final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + + @override + void onInit() { + super.onInit(); + _initNotifications(); + _startPolling(); + } + + Future _initNotifications() async { + const DarwinInitializationSettings initializationSettingsDarwin = + DarwinInitializationSettings( + requestAlertPermission: false, + requestBadgePermission: false, + requestSoundPermission: false); + + final LinuxInitializationSettings initializationSettingsLinux = + LinuxInitializationSettings( + defaultActionName: 'Open notification', + defaultIcon: AssetsLinuxIcon('assets/icon/icon.png'), + ); + + String? windowsIconPath; + try { + if (Util.isWindows()) { + final byteData = await rootBundle.load('assets/icon/icon.ico'); + final tempDir = await getTemporaryDirectory(); + final file = File('${tempDir.path}/notification_icon.ico'); + await file.writeAsBytes(byteData.buffer + .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)); + windowsIconPath = file.path; + } + } catch (e) { + // Ignore + } + + final WindowsInitializationSettings initializationSettingsWindows = + WindowsInitializationSettings( + appName: 'Gopeed', + appUserModelId: 'com.gopeed.gopeed', + guid: '3c1bf3f4-3d91-4eaa-a33f-8705e71cf1ce', // unique guid + iconPath: windowsIconPath, + ); + + final InitializationSettings initializationSettings = + InitializationSettings( + macOS: initializationSettingsDarwin, + linux: initializationSettingsLinux, + windows: initializationSettingsWindows, + ); + + await flutterLocalNotificationsPlugin.initialize( + settings: initializationSettings, + ); + } + + @override + void onClose() { + _timer?.cancel(); + super.onClose(); + } + + void _startPolling() { + _timer = Timer.periodic(const Duration(seconds: 2), (timer) async { + try { + final config = appController.downloaderConfig.value; + if (!config.extra.desktopNotification) { + return; + } + + final tasks = await getTasks([ + Status.ready, + Status.running, + Status.pause, + Status.wait, + Status.error, + Status.done, + ]); + + for (var task in tasks) { + final prevStatus = _previousStatus[task.id]; + final currentStatus = task.status; + + if (prevStatus != null && prevStatus != currentStatus) { + if (currentStatus == Status.done) { + _showNotification( + title: 'notificationTaskDone'.tr, + body: task.name, + ); + } else if (currentStatus == Status.error) { + _showNotification( + title: 'notificationTaskError'.tr, + body: task.name, + ); + } + } + _previousStatus[task.id] = currentStatus; + } + + // Clean up deleted tasks from map + final currentTaskIds = tasks.map((t) => t.id).toSet(); + _previousStatus + .removeWhere((id, status) => !currentTaskIds.contains(id)); + } catch (e) { + // Ignored + } + }); + } + + int _notificationId = 0; + + Future _showNotification( + {required String title, required String body}) async { + const NotificationDetails notificationDetails = NotificationDetails( + macOS: DarwinNotificationDetails(), + linux: LinuxNotificationDetails(), + windows: WindowsNotificationDetails(), + ); + + await flutterLocalNotificationsPlugin.show( + id: _notificationId++, + title: title, + body: body, + notificationDetails: notificationDetails, + ); + } +} diff --git a/ui/flutter/lib/i18n/langs/de_de.dart b/ui/flutter/lib/i18n/langs/de_de.dart index 7a26c42e..46ecaa3a 100644 --- a/ui/flutter/lib/i18n/langs/de_de.dart +++ b/ui/flutter/lib/i18n/langs/de_de.dart @@ -164,5 +164,8 @@ const deDE = { 'categoryPath': 'Kategoriepfad', 'builtInCategory': 'Integrierte Kategorie kann nicht gelöscht werden', 'selectCategory': 'Kategorie auswählen', + 'desktopNotification': 'Desktop-Benachrichtigungen', + 'notificationTaskDone': 'Aufgabe abgeschlossen', + 'notificationTaskError': 'Aufgabenfehler', }, }; diff --git a/ui/flutter/lib/i18n/langs/en_us.dart b/ui/flutter/lib/i18n/langs/en_us.dart index de422488..8d2c30f2 100644 --- a/ui/flutter/lib/i18n/langs/en_us.dart +++ b/ui/flutter/lib/i18n/langs/en_us.dart @@ -193,5 +193,8 @@ const enUS = { 'Task "@name" is waiting for URL update. Do you want to update it with the new URL?', 'pendingUpdateYes': 'Update Task', 'pendingUpdateNo': 'Create New Task', + 'desktopNotification': 'Desktop Notifications', + 'notificationTaskDone': 'Task completed', + 'notificationTaskError': 'Task error', }, }; diff --git a/ui/flutter/lib/i18n/langs/es_es.dart b/ui/flutter/lib/i18n/langs/es_es.dart index e9029d57..c4e9a595 100644 --- a/ui/flutter/lib/i18n/langs/es_es.dart +++ b/ui/flutter/lib/i18n/langs/es_es.dart @@ -142,5 +142,8 @@ const esES = { 'categoryPath': 'Ruta de categoría', 'builtInCategory': 'La categoría incorporada no se puede eliminar', 'selectCategory': 'Seleccionar categoría', + 'desktopNotification': 'Notificaciones de escritorio', + 'notificationTaskDone': 'Tarea completada', + 'notificationTaskError': 'Error de tarea', }, }; diff --git a/ui/flutter/lib/i18n/langs/fa_ir.dart b/ui/flutter/lib/i18n/langs/fa_ir.dart index 8612520a..067719f9 100644 --- a/ui/flutter/lib/i18n/langs/fa_ir.dart +++ b/ui/flutter/lib/i18n/langs/fa_ir.dart @@ -93,5 +93,8 @@ const faIR = { 'launchAtStartup': 'راه‌اندازی در استارت‌آپ', 'runAsMenubarApp': 'اجرا به عنوان برنامه نوار منو', 'runAsMenubarAppDesc': 'پنهان کردن آیکون Dock و اجرا فقط در نوار منو', + 'desktopNotification': 'اعلان‌های دسکتاپ', + 'notificationTaskDone': 'وظیفه تکمیل شد', + 'notificationTaskError': 'خطای وظیفه', } }; diff --git a/ui/flutter/lib/i18n/langs/fr_fr.dart b/ui/flutter/lib/i18n/langs/fr_fr.dart index 68e2fffc..058660e3 100644 --- a/ui/flutter/lib/i18n/langs/fr_fr.dart +++ b/ui/flutter/lib/i18n/langs/fr_fr.dart @@ -135,5 +135,8 @@ const frFR = { 'categoryPath': 'Chemin de la catégorie', 'builtInCategory': 'La catégorie intégrée ne peut pas être supprimée', 'selectCategory': 'Sélectionner une catégorie', + 'desktopNotification': 'Notifications de bureau', + 'notificationTaskDone': 'Tâche terminée', + 'notificationTaskError': 'Erreur de tâche', }, }; diff --git a/ui/flutter/lib/i18n/langs/hu_hu.dart b/ui/flutter/lib/i18n/langs/hu_hu.dart index 8c727d63..d0367ce5 100644 --- a/ui/flutter/lib/i18n/langs/hu_hu.dart +++ b/ui/flutter/lib/i18n/langs/hu_hu.dart @@ -156,5 +156,8 @@ const huHU = { 'categoryPath': 'Kategória útvonala', 'builtInCategory': 'A beépített kategória nem törölhető', 'selectCategory': 'Kategória kiválasztása', + 'desktopNotification': 'Asztali értesítések', + 'notificationTaskDone': 'Feladat befejezve', + 'notificationTaskError': 'Feladat hiba', }, }; diff --git a/ui/flutter/lib/i18n/langs/id_id.dart b/ui/flutter/lib/i18n/langs/id_id.dart index 64dfa9dc..23627a76 100644 --- a/ui/flutter/lib/i18n/langs/id_id.dart +++ b/ui/flutter/lib/i18n/langs/id_id.dart @@ -142,5 +142,8 @@ const idID = { 'categoryPath': 'Path Kategori', 'builtInCategory': 'Kategori bawaan tidak dapat dihapus', 'selectCategory': 'Pilih Kategori', + 'desktopNotification': 'Notifikasi Desktop', + 'notificationTaskDone': 'Tugas selesai', + 'notificationTaskError': 'Tugas error', }, }; diff --git a/ui/flutter/lib/i18n/langs/it_it.dart b/ui/flutter/lib/i18n/langs/it_it.dart index 530e4030..4955cf6a 100644 --- a/ui/flutter/lib/i18n/langs/it_it.dart +++ b/ui/flutter/lib/i18n/langs/it_it.dart @@ -130,5 +130,8 @@ const itIT = { 'categoryPath': 'Percorso categoria', 'builtInCategory': 'La categoria predefinita non può essere eliminata', 'selectCategory': 'Seleziona categoria', + 'desktopNotification': 'Notifiche Desktop', + 'notificationTaskDone': 'Attività completata', + 'notificationTaskError': 'Errore attività', }, }; diff --git a/ui/flutter/lib/i18n/langs/ja_jp.dart b/ui/flutter/lib/i18n/langs/ja_jp.dart index c10864c4..ab9e8a42 100644 --- a/ui/flutter/lib/i18n/langs/ja_jp.dart +++ b/ui/flutter/lib/i18n/langs/ja_jp.dart @@ -95,5 +95,8 @@ const jaJP = { 'launchAtStartup': '起動時に起動', 'runAsMenubarApp': 'メニューバーアプリとして実行', 'runAsMenubarAppDesc': 'Dockアイコンを非表示にして、メニューバーのみで実行', + 'desktopNotification': 'デスクトップ通知', + 'notificationTaskDone': 'タスク完了', + 'notificationTaskError': 'タスクエラー', } }; diff --git a/ui/flutter/lib/i18n/langs/pl_pl.dart b/ui/flutter/lib/i18n/langs/pl_pl.dart index 9fa613d5..6721099b 100644 --- a/ui/flutter/lib/i18n/langs/pl_pl.dart +++ b/ui/flutter/lib/i18n/langs/pl_pl.dart @@ -127,5 +127,8 @@ const plPL = { 'categoryPath': 'Ścieżka kategorii', 'builtInCategory': 'Wbudowanej kategorii nie można usunąć', 'selectCategory': 'Wybierz kategorię', + 'desktopNotification': 'Powiadomienia na pulpicie', + 'notificationTaskDone': 'Zadanie zakończone', + 'notificationTaskError': 'Błąd zadania', }, }; diff --git a/ui/flutter/lib/i18n/langs/pt_br.dart b/ui/flutter/lib/i18n/langs/pt_br.dart index 1e7b3273..760b8753 100644 --- a/ui/flutter/lib/i18n/langs/pt_br.dart +++ b/ui/flutter/lib/i18n/langs/pt_br.dart @@ -153,5 +153,8 @@ const ptBR = { 'categoryPath': 'Caminho da Categoria', 'builtInCategory': 'Categoria integrada não pode ser excluída', 'selectCategory': 'Selecionar Categoria', + 'desktopNotification': 'Notificações na área de trabalho', + 'notificationTaskDone': 'Tarefa concluída', + 'notificationTaskError': 'Erro na tarefa', }, }; diff --git a/ui/flutter/lib/i18n/langs/ru_ru.dart b/ui/flutter/lib/i18n/langs/ru_ru.dart index e85496aa..8103718a 100644 --- a/ui/flutter/lib/i18n/langs/ru_ru.dart +++ b/ui/flutter/lib/i18n/langs/ru_ru.dart @@ -152,5 +152,8 @@ const ruRU = { 'categoryPath': 'Путь категории', 'builtInCategory': 'Встроенную категорию нельзя удалить', 'selectCategory': 'Выбрать категорию', + 'desktopNotification': 'Уведомления', + 'notificationTaskDone': 'Задача завершена', + 'notificationTaskError': 'Ошибка задачи', } }; diff --git a/ui/flutter/lib/i18n/langs/ta_ta.dart b/ui/flutter/lib/i18n/langs/ta_ta.dart index 56694a9d..78a0cfbe 100644 --- a/ui/flutter/lib/i18n/langs/ta_ta.dart +++ b/ui/flutter/lib/i18n/langs/ta_ta.dart @@ -129,5 +129,8 @@ const taTA = { 'categoryPath': 'வகை பாதை', 'builtInCategory': 'உள்ளமைக்கப்பட்ட வகையை நீக்க முடியாது', 'selectCategory': 'வகையைத் தேர்ந்தெடுக்கவும்', + 'desktopNotification': 'கணினி அறிவிப்புகள்', + 'notificationTaskDone': 'பணி முடிந்தது', + 'notificationTaskError': 'பணி பிழை', }, }; diff --git a/ui/flutter/lib/i18n/langs/tr_tr.dart b/ui/flutter/lib/i18n/langs/tr_tr.dart index f8afb5f4..bcc9b792 100644 --- a/ui/flutter/lib/i18n/langs/tr_tr.dart +++ b/ui/flutter/lib/i18n/langs/tr_tr.dart @@ -193,5 +193,8 @@ const trTR = { '"@name" işlemi URL güncellemesi bekliyor. Yeni URL ile güncellemek istiyor musunuz?', 'pendingUpdateYes': 'İşlemi Güncelle', 'pendingUpdateNo': 'Yeni İşlem Oluştur', + 'desktopNotification': 'Masaüstü Bildirimleri', + 'notificationTaskDone': 'Görev tamamlandı', + 'notificationTaskError': 'Görev hatası', }, }; diff --git a/ui/flutter/lib/i18n/langs/uk_ua.dart b/ui/flutter/lib/i18n/langs/uk_ua.dart index 983dbc1b..6971776c 100644 --- a/ui/flutter/lib/i18n/langs/uk_ua.dart +++ b/ui/flutter/lib/i18n/langs/uk_ua.dart @@ -152,5 +152,8 @@ const ukUA = { 'categoryPath': 'Шлях категорії', 'builtInCategory': 'Вбудовану категорію неможливо видалити', 'selectCategory': 'Вибрати категорію', + 'desktopNotification': 'Сповіщення', + 'notificationTaskDone': 'Завдання завершено', + 'notificationTaskError': 'Помилка завдання', }, }; diff --git a/ui/flutter/lib/i18n/langs/vi_vn.dart b/ui/flutter/lib/i18n/langs/vi_vn.dart index 687a9837..b2759313 100644 --- a/ui/flutter/lib/i18n/langs/vi_vn.dart +++ b/ui/flutter/lib/i18n/langs/vi_vn.dart @@ -122,5 +122,8 @@ const viVN = { 'launchAtStartup': 'Khởi động cùng hệ thống', 'runAsMenubarApp': 'Chạy như ứng dụng thanh menu', 'runAsMenubarAppDesc': 'Ẩn biểu tượng Dock và chỉ chạy trong thanh menu', + 'desktopNotification': 'Thông báo trên màn hình', + 'notificationTaskDone': 'Nhiệm vụ hoàn thành', + 'notificationTaskError': 'Lỗi nhiệm vụ', }, }; diff --git a/ui/flutter/lib/i18n/langs/zh_cn.dart b/ui/flutter/lib/i18n/langs/zh_cn.dart index 98545381..97b0182f 100644 --- a/ui/flutter/lib/i18n/langs/zh_cn.dart +++ b/ui/flutter/lib/i18n/langs/zh_cn.dart @@ -188,5 +188,8 @@ const zhCN = { 'pendingUpdateConfirm': '任务 "@name" 正在等待更新地址,是否使用新地址更新该任务?', 'pendingUpdateYes': '更新任务', 'pendingUpdateNo': '创建新任务', + 'desktopNotification': '桌面通知', + 'notificationTaskDone': '任务完成', + 'notificationTaskError': '任务失败', } }; diff --git a/ui/flutter/lib/i18n/langs/zh_tw.dart b/ui/flutter/lib/i18n/langs/zh_tw.dart index 19113611..004e2f71 100644 --- a/ui/flutter/lib/i18n/langs/zh_tw.dart +++ b/ui/flutter/lib/i18n/langs/zh_tw.dart @@ -147,5 +147,8 @@ const zhTW = { 'categoryPath': '分類路徑', 'builtInCategory': '內建分類無法刪除', 'selectCategory': '選擇分類', + 'desktopNotification': '桌面通知', + 'notificationTaskDone': '任務完成', + 'notificationTaskError': '任務失敗', } }; diff --git a/ui/flutter/pubspec.lock b/ui/flutter/pubspec.lock index a40da66b..cfb257d3 100644 --- a/ui/flutter/pubspec.lock +++ b/ui/flutter/pubspec.lock @@ -265,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 + url: "https://pub.dev" + source: hosted + version: "0.7.12" desktop_drop: dependency: "direct main" description: @@ -406,6 +414,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "2b50e938a275e1ad77352d6a25e25770f4130baa61eaf02de7a9a884680954ad" + url: "https://pub.dev" + source: hosted + version: "20.1.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: dce0116868cedd2cdf768af0365fc37ff1cbef7c02c4f51d0587482e625868d0 + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "23de31678a48c084169d7ae95866df9de5c9d2a44be3e5915a2ff067aeeba899" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: e97a1a3016512437d9c0b12fae7d1491c3c7b9aa7f03a69b974308840656b02a + url: "https://pub.dev" + source: hosted + version: "2.0.1" flutter_localizations: dependency: "direct main" description: flutter @@ -1212,6 +1252,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7" + timezone: + dependency: transitive + description: + name: timezone + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 + url: "https://pub.dev" + source: hosted + version: "0.10.1" timing: dependency: transitive description: diff --git a/ui/flutter/pubspec.yaml b/ui/flutter/pubspec.yaml index b5d28fc9..bb59c80f 100644 --- a/ui/flutter/pubspec.yaml +++ b/ui/flutter/pubspec.yaml @@ -82,6 +82,7 @@ dependencies: ref: cf08af829f4a4145634f8a047108f505fdbe5eaa flutter_svg: ^2.0.17 dart_ipc: ^1.0.1 + flutter_local_notifications: 20.1.0 dependency_overrides: permission_handler_windows: git: