mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-09-01 07:04:07 +00:00
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
279 lines
8.8 KiB
Dart
279 lines
8.8 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
|
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:twonly/globals.dart';
|
|
import 'package:twonly/locator.dart';
|
|
import 'package:twonly/src/constants/routes.keys.dart';
|
|
import 'package:twonly/src/constants/secure_storage.keys.dart';
|
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
|
import 'package:twonly/src/localization/generated/app_localizations_de.dart';
|
|
import 'package:twonly/src/localization/generated/app_localizations_en.dart';
|
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
|
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
|
import 'package:twonly/src/providers/routing.provider.dart';
|
|
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
|
import 'package:twonly/src/utils/log.dart';
|
|
import 'package:twonly/src/utils/misc.dart';
|
|
|
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
|
FlutterLocalNotificationsPlugin();
|
|
|
|
Future<void> customLocalPushNotification(String title, String msg) async {
|
|
final androidNotificationDetails = AndroidNotificationDetails(
|
|
'1',
|
|
'System',
|
|
channelDescription: 'System messages.',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
styleInformation: BigTextStyleInformation(msg),
|
|
icon: 'ic_launcher_foreground',
|
|
);
|
|
|
|
const darwinNotificationDetails = DarwinNotificationDetails();
|
|
final notificationDetails = NotificationDetails(
|
|
android: androidNotificationDetails,
|
|
iOS: darwinNotificationDetails,
|
|
);
|
|
|
|
final id = Random.secure().nextInt(9999);
|
|
|
|
await flutterLocalNotificationsPlugin.show(
|
|
id,
|
|
title,
|
|
msg,
|
|
notificationDetails,
|
|
);
|
|
}
|
|
|
|
Future<void> showPushNotificationFromServerMessages(
|
|
int fromUserId,
|
|
EncryptedContent encryptedContent,
|
|
) async {
|
|
final pushData = await getPushNotificationFromEncryptedContent(
|
|
null, // this is the toUserID which must be null as this means that the targetMessageId was send from this user.
|
|
null,
|
|
encryptedContent,
|
|
);
|
|
if (pushData != null) {
|
|
final pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys);
|
|
for (final pushUser in pushUsers) {
|
|
if (pushUser.userId.toInt() == fromUserId) {
|
|
String? groupId;
|
|
if (encryptedContent.hasGroupId()) {
|
|
groupId = encryptedContent.groupId;
|
|
}
|
|
return showLocalPushNotification(pushUser, pushData, groupId: groupId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<PushNotification?> tryDecryptMessage(
|
|
List<int> key,
|
|
EncryptedPushNotification push,
|
|
) async {
|
|
try {
|
|
final chacha20 = FlutterChacha20.poly1305Aead();
|
|
final secretKeyData = SecretKeyData(key);
|
|
|
|
final secretBox = SecretBox(
|
|
push.ciphertext,
|
|
nonce: push.nonce,
|
|
mac: Mac(push.mac),
|
|
);
|
|
|
|
final plaintext = await chacha20.decrypt(
|
|
secretBox,
|
|
secretKey: secretKeyData,
|
|
);
|
|
return PushNotification.fromBuffer(plaintext);
|
|
} catch (e) {
|
|
// this error is allowed to happen...
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> showLocalPushNotification(
|
|
PushUser pushUser,
|
|
PushNotification pushNotification, {
|
|
String? groupId,
|
|
}) async {
|
|
String? title;
|
|
String? body;
|
|
|
|
// do not show notification for blocked users...
|
|
if (pushUser.blocked) {
|
|
Log.info('Blocked a message from a blocked user!');
|
|
return;
|
|
}
|
|
|
|
var targetGroupId = groupId;
|
|
if (targetGroupId == null) {
|
|
try {
|
|
if (userService.isUserCreated) {
|
|
targetGroupId = getUUIDforDirectChat(
|
|
userService.currentUser.userId,
|
|
pushUser.userId.toInt(),
|
|
);
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
|
|
if (targetGroupId != null) {
|
|
try {
|
|
final currentUri = routerProvider.routerDelegate.currentConfiguration.uri;
|
|
final isResumed =
|
|
WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed;
|
|
if (isResumed && currentUri.path.contains(targetGroupId)) {
|
|
Log.info(
|
|
'Suppressing local push notification because chat with group $targetGroupId is currently open.',
|
|
);
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
Log.error('Error checking current route: $e');
|
|
}
|
|
}
|
|
|
|
title = pushUser.displayName;
|
|
body = getPushNotificationText(pushNotification);
|
|
if (body == '') {
|
|
Log.error('No push notification type defined!');
|
|
}
|
|
|
|
FilePathAndroidBitmap? styleInformation;
|
|
final avatarPath = await getAvatarIcon(pushUser.userId.toInt());
|
|
if (avatarPath != null) {
|
|
styleInformation = FilePathAndroidBitmap(avatarPath);
|
|
}
|
|
|
|
final lang = getLocalizations();
|
|
|
|
final androidNotificationDetails = AndroidNotificationDetails(
|
|
'0',
|
|
lang.notificationCategoryMessageTitle,
|
|
channelDescription: lang.notificationCategoryMessageDesc,
|
|
importance: Importance.max,
|
|
priority: Priority.max,
|
|
ticker: 'You got a new message.',
|
|
largeIcon: styleInformation,
|
|
icon: 'ic_launcher_foreground',
|
|
groupKey: 'com.twonly.messages',
|
|
);
|
|
|
|
const darwinNotificationDetails = DarwinNotificationDetails();
|
|
final notificationDetails = NotificationDetails(
|
|
android: androidNotificationDetails,
|
|
iOS: darwinNotificationDetails,
|
|
);
|
|
|
|
String? payload;
|
|
|
|
if (groupId != null &&
|
|
(pushNotification.kind == PushKind.TEXT ||
|
|
pushNotification.kind == PushKind.RESPONSE ||
|
|
pushNotification.kind == PushKind.REACTION_TO_AUDIO ||
|
|
pushNotification.kind == PushKind.STORED_MEDIA_FILE ||
|
|
pushNotification.kind == PushKind.REACTION_TO_IMAGE ||
|
|
pushNotification.kind == PushKind.REACTION_TO_TEXT ||
|
|
pushNotification.kind == PushKind.REACTION_TO_VIDEO)) {
|
|
payload = Routes.chatsMessages(groupId);
|
|
} else {
|
|
payload = Routes.chats;
|
|
}
|
|
|
|
await flutterLocalNotificationsPlugin.show(
|
|
// Invalid argument (id): must fit within the size of a 32-bit integer
|
|
pushUser.userId.toInt() % 2147483647,
|
|
title,
|
|
body,
|
|
notificationDetails,
|
|
payload: payload,
|
|
);
|
|
|
|
if (Platform.isAndroid) {
|
|
final summaryAndroidDetails = AndroidNotificationDetails(
|
|
'0',
|
|
lang.notificationCategoryMessageTitle,
|
|
channelDescription: lang.notificationCategoryMessageDesc,
|
|
importance: Importance.max,
|
|
priority: Priority.max,
|
|
groupKey: 'com.twonly.messages',
|
|
setAsGroupSummary: true,
|
|
icon: 'ic_launcher_foreground',
|
|
);
|
|
final summaryNotificationDetails = NotificationDetails(
|
|
android: summaryAndroidDetails,
|
|
);
|
|
await flutterLocalNotificationsPlugin.show(
|
|
0,
|
|
lang.notificationCategoryMessageTitle,
|
|
'',
|
|
summaryNotificationDetails,
|
|
payload: Routes.chats,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<String?> getAvatarIcon(int contactId) async {
|
|
final avatarsDirectory = Directory('${AppEnvironment.cacheDir}/avatars');
|
|
final filePath = '${avatarsDirectory.path}/$contactId.png';
|
|
final file = File(filePath);
|
|
if (file.existsSync()) {
|
|
return filePath;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
AppLocalizations getLocalizations() {
|
|
final systemLanguage = Platform.localeName;
|
|
if (systemLanguage.contains('de')) return AppLocalizationsDe();
|
|
return AppLocalizationsEn();
|
|
}
|
|
|
|
String getPushNotificationText(PushNotification pushNotification) {
|
|
final lang = getLocalizations();
|
|
|
|
var inGroup = '';
|
|
|
|
if (pushNotification.hasAdditionalContent()) {
|
|
inGroup =
|
|
' ${lang.notificationFillerIn} ${pushNotification.additionalContent}';
|
|
}
|
|
|
|
final pushNotificationText = {
|
|
PushKind.TEXT.name: lang.notificationText(inGroup),
|
|
PushKind.TWONLY.name: lang.notificationTwonly(inGroup),
|
|
PushKind.VIDEO.name: lang.notificationVideo(inGroup),
|
|
PushKind.IMAGE.name: lang.notificationImage(inGroup),
|
|
PushKind.AUDIO.name: lang.notificationAudio(inGroup),
|
|
PushKind.CONTACT_REQUEST.name: lang.notificationContactRequest,
|
|
PushKind.ACCEPT_REQUEST.name: lang.notificationAcceptRequest,
|
|
PushKind.STORED_MEDIA_FILE.name: lang.notificationStoredMediaFile,
|
|
PushKind.REACTION.name: lang.notificationReaction,
|
|
PushKind.REOPENED_MEDIA.name: lang.notificationReopenedMedia,
|
|
PushKind.REACTION_TO_VIDEO.name: lang.notificationReactionToVideo(
|
|
pushNotification.additionalContent,
|
|
),
|
|
PushKind.REACTION_TO_AUDIO.name: lang.notificationReactionToAudio(
|
|
pushNotification.additionalContent,
|
|
),
|
|
PushKind.REACTION_TO_TEXT.name: lang.notificationReactionToText(
|
|
pushNotification.additionalContent,
|
|
),
|
|
PushKind.REACTION_TO_IMAGE.name: lang.notificationReactionToImage(
|
|
pushNotification.additionalContent,
|
|
),
|
|
PushKind.RESPONSE.name: lang.notificationResponse(inGroup),
|
|
PushKind.ADDED_TO_GROUP.name: lang.notificationAddedToGroup(
|
|
pushNotification.additionalContent,
|
|
),
|
|
};
|
|
|
|
return pushNotificationText[pushNotification.kind.name] ?? '';
|
|
}
|