diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index b62253ed9..ca45e6076 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -20,6 +20,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; +import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -73,6 +75,27 @@ class BackgroundPush { void _init() async { try { + if (PlatformInfos.isAndroid) { + final port = ReceivePort(); + IsolateNameServer.removePortNameMapping('background_tab_port'); + IsolateNameServer.registerPortWithName( + port.sendPort, + 'background_tab_port', + ); + port.listen( + (message) async { + try { + await notificationTap( + NotificationResponseJson.fromJsonString(message), + client: client, + router: FluffyChatApp.router, + ); + } catch (e, s) { + Logs().wtf('Main Notification Tap crashed', e, s); + } + }, + ); + } await _flutterLocalNotificationsPlugin.initialize( const InitializationSettings( android: AndroidInitializationSettings('notifications_icon'), diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart index 3387fdf38..09738b518 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart @@ -105,6 +105,7 @@ Future _constructDatabase(String clientName) async { version: 1, // most important : apply encryption when opening the DB onConfigure: helper?.applyPragmaKey, + singleInstance: false, ), ); diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart index e604ddcad..8c9a279ee 100644 --- a/lib/utils/notification_background_handler.dart +++ b/lib/utils/notification_background_handler.dart @@ -1,28 +1,87 @@ +import 'dart:convert'; +import 'dart:ui'; + import 'package:collection/collection.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; 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'; +bool _vodInitialized = false; + +extension NotificationResponseJson on NotificationResponse { + String toJsonString() => jsonEncode({ + 'type': notificationResponseType.name, + 'id': id, + 'actionId': actionId, + 'input': input, + 'payload': payload, + 'data': data, + }); + + static NotificationResponse fromJsonString(String jsonString) { + final json = jsonDecode(jsonString) as Map; + return NotificationResponse( + notificationResponseType: NotificationResponseType.values + .singleWhere((t) => t.name == json['type']), + id: json['id'] as int?, + actionId: json['actionId'] as String?, + input: json['input'] as String?, + payload: json['payload'] as String?, + data: json['data'] as Map, + ); + } +} + @pragma('vm:entry-point') void notificationTapBackground( NotificationResponse notificationResponse, ) async { + Logs().i('Notification tap in background'); + + final sendPort = IsolateNameServer.lookupPortByName('background_tab_port'); + if (sendPort != null) { + sendPort.send(notificationResponse.toJsonString()); + return; + } + + if (!_vodInitialized) { + await vod.init(); + _vodInitialized = true; + } final client = (await ClientManager.getClients( initialize: false, store: await SharedPreferences.getInstance(), )) .first; - notificationTap(notificationResponse, client: client); + await client.abortSync(); + await client.init( + waitForFirstSync: false, + waitUntilLoadCompletedLoaded: false, + ); + if (!client.isLogged()) { + throw Exception('Notification tab in background but not logged in!'); + } + try { + await notificationTap(notificationResponse, client: client); + } finally { + await client.dispose(closeDatabase: false); + } + return; } -void notificationTap( +Future notificationTap( NotificationResponse notificationResponse, { 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; @@ -56,6 +115,9 @@ void notificationTap( if (roomId == null) { throw Exception('Selected notification with action but no payload'); } + await client.roomsLoading; + await client.accountDataLoading; + await client.userDeviceKeysLoading; final room = client.getRoomById(roomId); if (room == null) { throw Exception( @@ -64,7 +126,11 @@ void notificationTap( } switch (actionType) { case FluffyChatNotificationActions.markAsRead: - await room.setReadMarker(room.lastEvent!.eventId); + await room.setReadMarker( + room.lastEvent!.eventId, + mRead: room.lastEvent!.eventId, + public: false, // TODO: Load preference here + ); case FluffyChatNotificationActions.reply: final input = notificationResponse.input; if (input == null || input.isEmpty) { diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index 078746c61..08de28ba6 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -279,16 +279,20 @@ Future _tryPushHelper( 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(), + AndroidNotificationActionInput( + label: l10n.writeAMessage, + ), ], + cancelNotification: false, + allowGeneratedReplies: true, + ), + AndroidNotificationAction( + FluffyChatNotificationActions.markAsRead.name, + l10n.markAsRead, ), ], );