Skip to content

Commit 667e15a

Browse files
committed
feat: notifications for windows with option to control download notifications
ref: #14
1 parent e34580f commit 667e15a

File tree

12 files changed

+192
-64
lines changed

12 files changed

+192
-64
lines changed

lib/controller/json_to_history_parser.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ class JsonToHistoryParser {
420420
final startTime = DateTime.now();
421421
_notificationTimer?.cancel();
422422
_notificationTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
423-
NotificationService.importHistoryNotification(parsedHistoryJson.value, totalJsonToParse.value, startTime);
423+
NotificationManager.instance.importHistoryNotification(parsedHistoryJson.value, totalJsonToParse.value, startTime);
424424
});
425425

426426
final datesAdded = <int>[];
@@ -481,7 +481,7 @@ class JsonToHistoryParser {
481481
isParsing.value = false;
482482

483483
_notificationTimer?.cancel();
484-
NotificationService.doneImportingHistoryNotification(parsedHistoryJson.value, addedHistoryJsonToPlaylist.value);
484+
NotificationManager.instance.doneImportingHistoryNotification(parsedHistoryJson.value, addedHistoryJsonToPlaylist.value);
485485

486486
_latestMissingMap.value = allMissingEntries;
487487
_latestMissingMapAddedStatus.clear();

lib/controller/notification_controller.dart

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ import 'package:flutter/material.dart';
33
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
44

55
import 'package:namida/controller/json_to_history_parser.dart';
6+
import 'package:namida/controller/platform/base.dart';
7+
import 'package:namida/controller/settings_controller.dart';
8+
import 'package:namida/core/constants.dart';
9+
import 'package:namida/core/enums.dart';
610
import 'package:namida/core/extensions.dart';
711
import 'package:namida/youtube/class/download_task_base.dart';
812

9-
class NotificationService {
10-
static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
13+
class NotificationManager {
14+
static final instance = NotificationManager._platform();
15+
const NotificationManager._();
1116

12-
static Future<void> cancelAll() async {
13-
try {
14-
return _flutterLocalNotificationsPlugin.cancelAll();
15-
} catch (_) {}
17+
static NotificationManager _platform() {
18+
return NamidaPlatformBuilder.init(
19+
android: () => const NotificationManager._(),
20+
windows: () => const _NotificationManagerSuppressed._(),
21+
);
1622
}
1723

24+
static final _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
25+
1826
static const _historyImportID = 1;
1927
static const _historyImportPayload = 'history_import';
2028
static const _historyImportChannelName = 'History Import';
@@ -26,13 +34,21 @@ class NotificationService {
2634
static const _youtubeDownloadChannelDescription = 'Downlaod content from youtube';
2735

2836
static Future<bool?> init() {
29-
return _flutterLocalNotificationsPlugin.initialize(
30-
const InitializationSettings(
37+
final didInit = _flutterLocalNotificationsPlugin.initialize(
38+
InitializationSettings(
3139
android: AndroidInitializationSettings('ic_stat_musicnote'),
40+
windows: WindowsInitializationSettings(
41+
appName: 'Namida',
42+
appUserModelId: 'namidaco.namida.notification',
43+
guid: '51435cfe-f7be-4a73-82c1-50d53a8e7ae6',
44+
iconPath: AppPaths.NAMIDA_LOGO_MONET,
45+
),
3246
),
3347
onDidReceiveBackgroundNotificationResponse: _onDidReceiveLocalNotification,
3448
onDidReceiveNotificationResponse: _onDidReceiveLocalNotification,
3549
);
50+
_flutterLocalNotificationsPlugin.cancelAll();
51+
return didInit;
3652
}
3753

3854
static void mediaNotification({
@@ -84,7 +100,7 @@ class NotificationService {
84100
);
85101
}
86102

87-
static void downloadYoutubeNotification({
103+
void downloadYoutubeNotification({
88104
required DownloadTaskFilename filenameWrapper,
89105
required String title,
90106
required String Function(String progressText) subtitle,
@@ -111,11 +127,11 @@ class NotificationService {
111127
);
112128
}
113129

114-
static Future<void> removeDownloadingYoutubeNotification({required DownloadTaskFilename filenameWrapper}) async {
130+
Future<void> removeDownloadingYoutubeNotification({required DownloadTaskFilename filenameWrapper}) async {
115131
await _flutterLocalNotificationsPlugin.cancel(_youtubeDownloadID, tag: filenameWrapper.key);
116132
}
117133

118-
static void doneDownloadingYoutubeNotification({
134+
void doneDownloadingYoutubeNotification({
119135
required DownloadTaskFilename filenameWrapper,
120136
required String videoTitle,
121137
required String subtitle,
@@ -139,7 +155,7 @@ class NotificationService {
139155
);
140156
}
141157

142-
static void importHistoryNotification(int parsed, int total, DateTime displayTime) {
158+
void importHistoryNotification(int parsed, int total, DateTime displayTime) {
143159
_createProgressNotification(
144160
id: _historyImportID,
145161
progress: parsed,
@@ -154,7 +170,7 @@ class NotificationService {
154170
);
155171
}
156172

157-
static void doneImportingHistoryNotification(int totalParsed, int totalAdded) {
173+
void doneImportingHistoryNotification(int totalParsed, int totalAdded) {
158174
_createNotification(
159175
id: _historyImportID,
160176
title: 'Done importing history',
@@ -174,7 +190,7 @@ class NotificationService {
174190
}
175191
}
176192

177-
static void _createNotification({
193+
void _createNotification({
178194
required int id,
179195
required String title,
180196
required String body,
@@ -270,3 +286,47 @@ class NotificationService {
270286
);
271287
}
272288
}
289+
290+
class _NotificationManagerSuppressed extends NotificationManager {
291+
const _NotificationManagerSuppressed._() : super._();
292+
293+
DownloadNotifications get _downloadNotifications => settings.youtube.downloadNotifications.value;
294+
295+
@override
296+
void downloadYoutubeNotification({
297+
required DownloadTaskFilename filenameWrapper,
298+
required String title,
299+
required String Function(String progressText) subtitle,
300+
String? imagePath,
301+
required int progress,
302+
required int total,
303+
required DateTime displayTime,
304+
required bool isRunning,
305+
}) {
306+
return;
307+
}
308+
309+
@override
310+
void doneDownloadingYoutubeNotification({
311+
required DownloadTaskFilename filenameWrapper,
312+
required String videoTitle,
313+
required String subtitle,
314+
required bool failed,
315+
String? imagePath,
316+
}) async {
317+
if (_downloadNotifications == DownloadNotifications.disableAll) return;
318+
if (_downloadNotifications == DownloadNotifications.showFailedOnly && !failed) return;
319+
super.doneDownloadingYoutubeNotification(
320+
filenameWrapper: filenameWrapper,
321+
videoTitle: videoTitle,
322+
subtitle: subtitle,
323+
failed: failed,
324+
imagePath: imagePath,
325+
);
326+
}
327+
328+
@override
329+
void importHistoryNotification(int parsed, int total, DateTime displayTime) {
330+
return;
331+
}
332+
}

lib/controller/settings.youtube.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class _YoutubeSettings with SettingsFileWriter {
3030
final downloadFilenameBuilder = _defaultFilenameBuilder.obs;
3131
final initialDefaultMetadataTags = <String, String>{};
3232

33+
// -- currently used for windows
34+
final downloadNotifications = DownloadNotifications.showFailedOnly.obs;
35+
3336
bool markVideoWatched = true;
3437
InnertubeClients? innertubeClient;
3538
bool whiteVideoBGInLightMode = false;
@@ -55,6 +58,7 @@ class _YoutubeSettings with SettingsFileWriter {
5558
YTSeekActionMode? tapToSeek,
5659
YTSeekActionMode? dragToSeek,
5760
String? downloadFilenameBuilder,
61+
DownloadNotifications? downloadNotifications,
5862
bool? markVideoWatched,
5963
InnertubeClients? innertubeClient,
6064
bool setDefaultInnertubeClient = false,
@@ -81,6 +85,7 @@ class _YoutubeSettings with SettingsFileWriter {
8185
if (tapToSeek != null) this.tapToSeek.value = tapToSeek;
8286
if (dragToSeek != null) this.dragToSeek.value = dragToSeek;
8387
if (downloadFilenameBuilder != null) this.downloadFilenameBuilder.value = downloadFilenameBuilder;
88+
if (downloadNotifications != null) this.downloadNotifications.value = downloadNotifications;
8489

8590
if (markVideoWatched != null) this.markVideoWatched = markVideoWatched;
8691
if (innertubeClient != null || setDefaultInnertubeClient) this.innertubeClient = innertubeClient;
@@ -144,6 +149,7 @@ class _YoutubeSettings with SettingsFileWriter {
144149
ytVisibleShorts.value = (json['ytVisibleShorts'] as Map?)?.map((key, value) => MapEntry(YTVisibleShortPlaces.values.getEnum(key)!, value)) ?? ytVisibleShorts.value;
145150
ytVisibleMixes.value = (json['ytVisibleMixes'] as Map?)?.map((key, value) => MapEntry(YTVisibleMixesPlaces.values.getEnum(key)!, value)) ?? ytVisibleMixes.value;
146151
downloadFilenameBuilder.value = json['downloadFilenameBuilder'] ?? downloadFilenameBuilder.value;
152+
downloadNotifications.value = DownloadNotifications.values.getEnum(json['downloadNotifications']) ?? downloadNotifications.value;
147153

148154
final initialDefaultMetadataTagsInStorage = (json['initialDefaultMetadataTags'] as Map?);
149155
if (initialDefaultMetadataTagsInStorage != null) {
@@ -184,6 +190,7 @@ class _YoutubeSettings with SettingsFileWriter {
184190
'ytVisibleShorts': ytVisibleShorts.map((key, value) => MapEntry(key.name, value)),
185191
'ytVisibleMixes': ytVisibleMixes.map((key, value) => MapEntry(key.name, value)),
186192
'downloadFilenameBuilder': downloadFilenameBuilder.value,
193+
'downloadNotifications': downloadNotifications.value.name,
187194
'initialDefaultMetadataTags': initialDefaultMetadataTags,
188195
'markVideoWatched': markVideoWatched,
189196
'innertubeClient': innertubeClient?.name,

lib/core/constants.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ extension PathTypeUtils on String {
516516
class NamidaFeaturesVisibility {
517517
static final _platform = defaultTargetPlatform;
518518
static final _isAndroid = _platform == TargetPlatform.android;
519+
static final _isWindows = _platform == TargetPlatform.windows;
519520

520521
static final wallpaperColors = _isAndroid && NamidaDeviceInfo.sdkVersion >= 31;
521522
static final displayArtworkOnLockscreen = _isAndroid && NamidaDeviceInfo.sdkVersion < 33;
@@ -534,4 +535,6 @@ class NamidaFeaturesVisibility {
534535

535536
static final onAudioQueryAvailable = _isAndroid;
536537
static final recieveSharingIntents = _isAndroid;
538+
539+
static final showDownloadNotifications = _isWindows;
537540
}

lib/core/enums.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,9 @@ enum YTSortType {
443443
latestPlayed,
444444
firstListen,
445445
}
446+
447+
enum DownloadNotifications {
448+
disableAll,
449+
showAll,
450+
showFailedOnly,
451+
}

lib/core/namida_converter_ext.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,10 @@ extension PlaylistPrivacyUtils on PlaylistPrivacy {
684684
String toText() => _NamidaConverters.inst.getTitle(this);
685685
}
686686

687+
extension DownloadNotificationsUtils on DownloadNotifications {
688+
String toText() => _NamidaConverters.inst.getTitle(this);
689+
}
690+
687691
extension RouteUtils on NamidaRoute {
688692
List<Selectable> tracksListInside() {
689693
final iter = tracksInside();
@@ -1397,6 +1401,11 @@ class _NamidaConverters {
13971401
PlaylistPrivacy.unlisted: lang.UNLISTED,
13981402
PlaylistPrivacy.private: lang.PRIVATE,
13991403
},
1404+
DownloadNotifications: {
1405+
DownloadNotifications.disableAll: lang.DISABLE_ALL,
1406+
DownloadNotifications.showAll: lang.SHOW_ALL,
1407+
DownloadNotifications.showFailedOnly: lang.SHOW_FAILED_ONLY,
1408+
},
14001409
};
14011410

14021411
// ====================================================

lib/core/translations/keys.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ abstract class LanguageKeys {
154154
String get DIM_INTENSITY => _getKey('DIM_INTENSITY');
155155
String get DIM_MINIPLAYER_AFTER_SECONDS => _getKey('DIM_MINIPLAYER_AFTER_SECONDS');
156156
String get DIRECTORY_DOESNT_EXIST => _getKey('DIRECTORY_DOESNT_EXIST');
157+
String get DISABLE_ALL => _getKey('DISABLE_ALL');
157158
String get DISABLE_REORDERING => _getKey('DISABLE_REORDERING');
158159
String get DISABLE_SEARCH_CLEANUP => _getKey('DISABLE_SEARCH_CLEANUP');
159160
String get DISC_NUMBER_TOTAL => _getKey('DISC_NUMBER_TOTAL');
@@ -601,7 +602,9 @@ abstract class LanguageKeys {
601602
String get SHOULD_DUCK_NOTE => _getKey('SHOULD_DUCK_NOTE');
602603
String get SHOULD_PAUSE => _getKey('SHOULD_PAUSE');
603604
String get SHOULD_PAUSE_NOTE => _getKey('SHOULD_PAUSE_NOTE');
605+
String get SHOW_ALL => _getKey('SHOW_ALL');
604606
String get SHOW_CHANNEL_WATERMARK_IN_FULLSCREEN => _getKey('SHOW_CHANNEL_WATERMARK_IN_FULLSCREEN');
607+
String get SHOW_FAILED_ONLY => _getKey('SHOW_FAILED_ONLY');
605608
String get SHOW_HIDE_UNKNOWN_FIELDS => _getKey('SHOW_HIDE_UNKNOWN_FIELDS');
606609
String get SHOW_MIX_PLAYLISTS_IN => _getKey('SHOW_MIX_PLAYLISTS_IN');
607610
String get SHOW_MORE => _getKey('SHOW_MORE');

lib/main.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,7 @@ void mainInitialization() async {
202202
NamidaNavigator.inst.setDefaultSystemUIOverlayStyle();
203203

204204
ScrollSearchController.inst.initialize();
205-
NotificationService.init();
206-
NotificationService.cancelAll();
205+
NotificationManager.init();
207206
FlutterVolumeController.updateShowSystemUI(false);
208207

209208
runApp(Namida(shouldShowOnBoarding: shouldShowOnBoarding));

lib/ui/widgets/custom_widgets.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ class CustomListTile extends StatelessWidget {
302302
? Text(
303303
trailingText!,
304304
style: context.textTheme.displayMedium?.copyWith(color: context.theme.colorScheme.onSurface.withAlpha(200)),
305+
maxLines: 1,
306+
overflow: TextOverflow.ellipsis,
307+
textAlign: TextAlign.center,
305308
)
306309
: trailing,
307310
),

0 commit comments

Comments
 (0)