From 1ccb1642ac58b15669180d5a9d868d04ffbc1a1f Mon Sep 17 00:00:00 2001 From: td Date: Sat, 11 Feb 2023 19:22:00 +0530 Subject: [PATCH] feat: notification actions android unified push notifications seem to stop after one aciton, this is broken atm --- assets/l10n/intl_en.arb | 4 ++- lib/config/app_config.dart | 3 ++ lib/utils/background_push.dart | 39 ++++++++++++++++++++++- lib/utils/push_helper.dart | 45 ++++++++++++++++++++++++--- pubspec.lock | 57 +++++++++++++++------------------- pubspec.yaml | 10 ++++++ 6 files changed, 120 insertions(+), 38 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 10875bf7..3e53ac76 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2518,5 +2518,7 @@ "enterInviteLinkOrMatrixId": "Enter invite link or Matrix ID...", "reopenChat": "Reopen chat", "noBackupWarning": "Warning! Without enabling chat backup, you will lose access to your encrypted messages. It is highly recommended to enable the chat backup first before logging out.", - "noOtherDevicesFound": "No other devices found" + "noOtherDevicesFound": "No other devices found", + "markRead": "Mark read", + "message": "Message" } \ No newline at end of file diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index bfe4c449..15398e4c 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -63,6 +63,9 @@ abstract class AppConfig { 'https://github.com/googlefonts/noto-emoji/'; static const double borderRadius = 16.0; static const double columnWidth = 360.0; + static const String markAsReadAction = 'markAsReadAction'; + static const String replyAction = 'replyAction'; + static const String notificationIsolate = 'notificationIsolate'; static void loadFromJson(Map json) { if (json['chat_color'] != null) { diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 49d8178b..2cafe27c 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -20,6 +20,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'dart:typed_data'; import 'dart:ui'; @@ -72,6 +73,16 @@ class BackgroundPush { bool upAction = false; BackgroundPush._(this.client) { + IsolateNameServer.removePortNameMapping(AppConfig.notificationIsolate); + final receivePort = ReceivePort(); + + IsolateNameServer.registerPortWithName( + receivePort.sendPort, AppConfig.notificationIsolate); + final backgroundMessagePort = receivePort.asBroadcastStream(); + backgroundMessagePort.listen((event) async { + await notificationActionCallbacks(event); + }); + onRoomSync ??= client.onSync.stream .where((s) => s.hasRoomUpdate) .listen((s) => _onClearingPush(getFromServer: false)); @@ -116,6 +127,29 @@ class BackgroundPush { } StreamSubscription? onRoomSync; + Future notificationActionCallbacks(Map response) async { + try { + if (response['payload'] == null) return; + Logs().d('Got response with action: ${response['actionId']}'); + final roomId = json.decode(response['payload']!)['roomId']; + final eventId = json.decode(response['payload']!)['eventId']; + if (response['actionId'] == AppConfig.markAsReadAction) { + Logs().d('notification action setting read marker to $eventId'); + await client.setReadMarker(roomId, eventId, mRead: eventId); + } + if (response['actionId'] == AppConfig.replyAction) { + Logs().d('notification action replying in room $roomId'); + final sentEventId = await client + .getRoomById(roomId)! + .sendTextEvent(response['input'].toString()); + if (sentEventId != null) { + await client.setReadMarker(roomId, sentEventId, mRead: sentEventId); + } + } + } catch (e) { + Logs().e('[BackgroundPushPlugin] error in notification actions', e); + } + } Future setupPusher({ String? gatewayUrl, @@ -282,7 +316,8 @@ class BackgroundPush { Future goToRoom(NotificationResponse? response) async { try { - final roomId = response?.payload; + if (response == null || response.payload == null) return; + final roomId = jsonDecode(response.payload!)['roomId']; Logs().v('[Push] Attempting to go to room $roomId...'); if (router == null || roomId == null) { return; @@ -369,11 +404,13 @@ class BackgroundPush { json.decode(utf8.decode(message))['notification']); // UP may strip the devices list data['devices'] ??= []; + Logs().e('showingaksdjf'); await pushHelper( PushNotification.fromJson(data), client: client, l10n: l10n, activeRoomId: router?.currentState?.pathParameters['roomid'], + onSelectNotification: goToRoom, ); } diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 242576f4..0b62e7ee 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -15,6 +15,21 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/voip/callkeep_manager.dart'; +// Is triggered by `onDidReceiveBackgroundNotificationResponse` in a different isolate +// this gets cached btw (until new or uninstall). so even if you disable +// `onDidReceiveBackgroundNotificationResponse` the old `notificationTapBackground` will +// still function until replaced or app is reinstalled +@pragma('vm:entry-point') +void notificationTapBackground(NotificationResponse notificationResponse) { + final sendPort = + IsolateNameServer.lookupPortByName(AppConfig.notificationIsolate); + sendPort!.send({ + 'payload': notificationResponse.payload, + 'actionId': notificationResponse.actionId, + 'input': notificationResponse.input, + }); +} + Future pushHelper( PushNotification notification, { Client? client, @@ -41,7 +56,7 @@ Future pushHelper( iOS: DarwinInitializationSettings(), ), onDidReceiveNotificationResponse: onSelectNotification, - onDidReceiveBackgroundNotificationResponse: onSelectNotification, + // onDidReceiveBackgroundNotificationResponse: onSelectNotification, ); flutterLocalNotificationsPlugin.show( 0, @@ -93,7 +108,7 @@ Future _tryPushHelper( iOS: DarwinInitializationSettings(), ), onDidReceiveNotificationResponse: onSelectNotification, - //onDidReceiveBackgroundNotificationResponse: onSelectNotification, + onDidReceiveBackgroundNotificationResponse: notificationTapBackground, ); client ??= (await ClientManager.getClients(initialize: false)).first; @@ -197,6 +212,29 @@ Future _tryPushHelper( AppConfig.pushNotificationsChannelName, channelDescription: AppConfig.pushNotificationsChannelDescription, number: notification.counts?.unread, + actions: event.room.membership == Membership.join + ? [ + if (event.room.canSendEvent('m.read') && + event.room.canSendEvent('m.fully_read')) + AndroidNotificationAction( + AppConfig.markAsReadAction, + l10n.markRead, + showsUserInterface: false, + ), + if (event.room.canSendEvent(EventTypes.Message)) + AndroidNotificationAction( + AppConfig.replyAction, + l10n.reply, + showsUserInterface: false, + inputs: [ + AndroidNotificationActionInput( + label: l10n.message, + allowedMimeTypes: {'text/plain'}, + ) + ], + ) + ] + : [], styleInformation: messagingStyleInformation ?? MessagingStyleInformation( Person(name: event.room.client.userID), @@ -208,7 +246,6 @@ Future _tryPushHelper( ), ticker: l10n.unreadChats(notification.counts?.unread ?? 1), importance: Importance.max, - priority: Priority.high, groupKey: event.room.id, ); const iOSPlatformChannelSpecifics = DarwinNotificationDetails(); @@ -224,7 +261,7 @@ Future _tryPushHelper( ), body, platformChannelSpecifics, - payload: event.roomId, + payload: json.encode({'roomId': event.roomId, 'eventId': event.eventId}), ); Logs().v('Push helper has been completed!'); } diff --git a/pubspec.lock b/pubspec.lock index 8e0342f2..de8f872d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: dbus - sha256: "4f814fc7e73057f78f307a6c4714fe2ffb4bdb994ab1970540a068ec4d5a45be" + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.8" desktop_drop: dependency: "direct main" description: @@ -614,19 +614,20 @@ packages: flutter_local_notifications: dependency: "direct main" description: - name: flutter_local_notifications - sha256: f222919a34545931e47b06000836b5101baeffb0e6eb5a4691d2d42851740dd9 - url: "https://pub.dev" - source: hosted - version: "12.0.4" + path: flutter_local_notifications + ref: "v13.1.0" + resolved-ref: af8082bc65d281e4485b41d0a8b72614627fbb1c + url: "https://gitlab.com/techno_disaster/flutter_local_notifications.git" + source: git + version: "13.1.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: "6af440e3962eeab8459602c309d7d4ab9e62f05d5cfe58195a28f846a0b5d523" + sha256: "8f6c1611e0c4a88a382691a97bb3c3feb24cc0c0b54152b8b5fb7ffb837f7fbf" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "3.0.0" flutter_local_notifications_platform_interface: dependency: transitive description: @@ -649,13 +650,13 @@ packages: source: hosted version: "3.1.0" flutter_math_fork: - dependency: transitive + dependency: "direct overridden" description: name: flutter_math_fork - sha256: cfec964c4975c6becc64291eb9b782fe70df5e0c5bfe0763d9e856432fcc6fcd + sha256: fa511bdcb91fc35eac155ec5790883886dd9de6572fea549f1ad4660ef6804e9 url: "https://pub.dev" source: hosted - version: "0.4.2+2" + version: "0.6.3+1" flutter_matrix_html: dependency: "direct main" description: @@ -668,10 +669,10 @@ packages: dependency: "direct dev" description: name: flutter_native_splash - sha256: bd36d1a7f05ff8378cad17d20c33ca904630bfd3fcf8b15c9e8237efbccfad0a + sha256: "6777a3abb974021a39b5fdd2d46a03ca390e03903b6351f21d10e7ecc969f12d" url: "https://pub.dev" source: hosted - version: "2.2.0+1" + version: "2.2.16" flutter_olm: dependency: "direct main" description: @@ -757,10 +758,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "9ac1967e2f72a08af11b05b39167920f90d043cf67163d13a544a358c8f31afa" + sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" url: "https://pub.dev" source: hosted - version: "0.22.0" + version: "1.1.6" flutter_test: dependency: "direct dev" description: flutter @@ -933,10 +934,10 @@ packages: dependency: "direct main" description: name: image - sha256: "02bafd3b4f399bfeb10034deba9753d93b55ce41cd0c4d3d8b355626f80e5b32" + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.3.0" image_picker: dependency: "direct main" description: @@ -1079,14 +1080,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.1" - lint: - dependency: transitive - description: - name: lint - sha256: "4a539aa34ec5721a2c7574ae2ca0336738ea4adc2a34887d54b7596310b33c85" - url: "https://pub.dev" - source: hosted - version: "1.10.0" lints: dependency: transitive description: @@ -1323,18 +1316,18 @@ packages: dependency: transitive description: name: path_drawing - sha256: "3bdd251dae9ffaef944450b73f168610db7e968e7b20daf0c3907f8b4aafc8a2" + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 url: "https://pub.dev" source: hosted - version: "0.5.1+1" + version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing - sha256: ee5c47c1058ad66b4a41746ec3996af9593d0858872807bcd64ac118f0700337 + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "1.0.1" path_provider: dependency: "direct main" description: @@ -2264,10 +2257,10 @@ packages: dependency: transitive description: name: xml - sha256: "80d494c09849dc3f899d227a78c30c5b949b985ededf884cb3f3bcd39f4b447a" + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "6.2.2" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7892f7ce..d1712f0b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -147,6 +147,16 @@ dependency_overrides: # This otherwise breaks on linux with flutter 3.7.0, let's override it for now. file_selector: ^0.9.2+2 file_selector_linux: ^0.9.1 + # because unified push and flutter isolate both have a flutter engine spawned + # automatically, this fork uses those cached engine for notification actions + # on android + flutter_local_notifications: + git: + url: https://gitlab.com/techno_disaster/flutter_local_notifications.git + path: flutter_local_notifications + ref: v13.1.0 + # dependency hell + flutter_math_fork: ^0.6.3+1 # fake secure storage plugin for Windows # See: https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/15161 flutter_secure_storage_windows: