From 1817b2944209480ad5b0e1552a2c05e381b8b984 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 3 Oct 2024 17:06:59 +0200 Subject: [PATCH 1/2] feat: Notification actions on android --- lib/utils/background_push.dart | 36 ++++----- .../notification_background_handler.dart | 75 +++++++++++++++++++ lib/utils/push_helper.dart | 14 ++++ 3 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 lib/utils/notification_background_handler.dart diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index a7ce8c6de..750a67fe0 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -32,6 +32,7 @@ import 'package:matrix/matrix.dart'; import 'package:unifiedpush/unifiedpush.dart'; import 'package:unifiedpush_ui/unifiedpush_ui.dart'; +import 'package:fluffychat/utils/notification_background_handler.dart'; import 'package:fluffychat/utils/push_helper.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import '../config/app_config.dart'; @@ -77,7 +78,12 @@ class BackgroundPush { android: AndroidInitializationSettings('notifications_icon'), iOS: DarwinInitializationSettings(), ), - onDidReceiveNotificationResponse: goToRoom, + onDidReceiveNotificationResponse: (response) => notificationTap( + response, + client: client, + router: FluffyChatApp.router, + ), + onDidReceiveBackgroundNotificationResponse: notificationTapBackground, ); Logs().v('Flutter Local Notifications initialized'); firebase?.setListeners( @@ -275,7 +281,14 @@ class BackgroundPush { return; } _wentToRoomOnStartup = true; - goToRoom(details.notificationResponse); + final response = details.notificationResponse; + if (response != null) { + notificationTap( + response, + client: client, + router: FluffyChatApp.router, + ); + } }); } @@ -319,25 +332,6 @@ class BackgroundPush { ); } - Future goToRoom(NotificationResponse? response) async { - try { - final roomId = response?.payload; - Logs().v('[Push] Attempting to go to room $roomId...'); - if (roomId == null) { - return; - } - await client.roomsLoading; - await client.accountDataLoading; - FluffyChatApp.router.go( - client.getRoomById(roomId)?.membership == Membership.invite - ? '/rooms' - : '/rooms/$roomId', - ); - } catch (e, s) { - Logs().e('[Push] Failed to open room', e, s); - } - } - Future setupUp() async { await UnifiedPushUi(matrix!.context, ["default"], UPFunctions()) .registerAppWithDialog(); diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart new file mode 100644 index 000000000..08fbba926 --- /dev/null +++ b/lib/utils/notification_background_handler.dart @@ -0,0 +1,75 @@ +import 'package:collection/collection.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:fluffychat/utils/client_manager.dart'; + +@pragma('vm:entry-point') +void notificationTapBackground( + NotificationResponse notificationResponse, +) async { + final client = (await ClientManager.getClients( + initialize: false, + store: await SharedPreferences.getInstance(), + )) + .first; + notificationTap(notificationResponse, client: client); +} + +void notificationTap( + NotificationResponse notificationResponse, { + GoRouter? router, + required Client client, +}) async { + switch (notificationResponse.notificationResponseType) { + case NotificationResponseType.selectedNotification: + final roomId = notificationResponse.payload; + if (roomId == null) return; + + if (router == null) { + Logs().v('Ignore select notification action in background mode'); + return; + } + Logs().v('Open room from notification tap', roomId); + await client.roomsLoading; + await client.accountDataLoading; + router.go( + client.getRoomById(roomId)?.membership == Membership.invite + ? '/rooms' + : '/rooms/$roomId', + ); + case NotificationResponseType.selectedNotificationAction: + final actionType = FluffyChatNotificationActions.values.singleWhereOrNull( + (action) => action.name == notificationResponse.actionId, + ); + if (actionType == null) { + throw Exception('Selected notification with action but no action ID'); + } + final roomId = notificationResponse.payload; + if (roomId == null) { + throw Exception('Selected notification with action but no payload'); + } + final room = client.getRoomById(roomId); + if (room == null) { + throw Exception( + 'Selected notification with action but unknown room $roomId', + ); + } + switch (actionType) { + case FluffyChatNotificationActions.markAsRead: + await room.setReadMarker(room.lastEvent!.eventId); + case FluffyChatNotificationActions.reply: + final input = notificationResponse.input; + if (input == null || input.isEmpty) { + throw Exception( + 'Selected notification with reply action but without input', + ); + } + await room.sendTextEvent(input); + } + } +} + +enum FluffyChatNotificationActions { markAsRead, reply } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index ff6c5934e..9cbc4aa67 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -14,6 +14,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/notification_background_handler.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/voip/callkeep_manager.dart'; @@ -274,6 +275,19 @@ Future _tryPushHelper( importance: Importance.high, priority: Priority.max, groupKey: event.room.spaceParents.firstOrNull?.roomId ?? 'rooms', + actions: [ + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.reply.name, + l10n.reply, + inputs: [ + const AndroidNotificationActionInput(), + ], + ), + ], ); const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); final platformChannelSpecifics = NotificationDetails( From fec0d2c0470ecc0cea62e3d05f674f162dfe5cc1 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 3 Oct 2024 17:33:44 +0200 Subject: [PATCH 2/2] feat: Add notification actions --- lib/utils/notification_background_handler.dart | 13 +++++++++++++ lib/utils/push_helper.dart | 4 +--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart index 08fbba926..a7d45fad5 100644 --- a/lib/utils/notification_background_handler.dart +++ b/lib/utils/notification_background_handler.dart @@ -10,11 +10,20 @@ import 'package:fluffychat/utils/client_manager.dart'; void notificationTapBackground( NotificationResponse notificationResponse, ) async { + Logs().i('Notification tap in background'); final client = (await ClientManager.getClients( initialize: false, store: await SharedPreferences.getInstance(), )) .first; + await client.abortSync(); + await client.init( + waitForFirstSync: false, + waitUntilLoadCompletedLoaded: false, + ); + if (!client.isLogged()) { + throw Exception('Notification tab in background but not logged in!'); + } notificationTap(notificationResponse, client: client); } @@ -23,6 +32,10 @@ void notificationTap( GoRouter? router, required Client client, }) async { + Logs().d( + 'Notification action handler started', + notificationResponse.notificationResponseType.name, + ); switch (notificationResponse.notificationResponseType) { case NotificationResponseType.selectedNotification: final roomId = notificationResponse.payload; diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 9cbc4aa67..1a0ec9f2e 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -283,9 +283,7 @@ Future _tryPushHelper( AndroidNotificationAction( FluffyChatNotificationActions.reply.name, l10n.reply, - inputs: [ - const AndroidNotificationActionInput(), - ], + inputs: [const AndroidNotificationActionInput()], ), ], );