From 55e845446c3928c821e3157d719ed3d064812278 Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Tue, 28 Apr 2020 08:37:50 +0200 Subject: [PATCH] Cleaned up and hardened BloC logic #501 OT-779 Fixed method channel names Fixed logger usage Renamed notification_manager.dart to display_notification_manager.dart Added async suffix Updated libraries involved in push and notifications Fixed some names and const declarations --- lib/src/chat/chat_bloc.dart | 6 +- lib/src/contact/contact_change.dart | 4 +- lib/src/extensions/numbers_apis.dart | 6 +- lib/src/invite/invite_bloc.dart | 11 +- lib/src/invite/invite_service.dart | 2 +- lib/src/l10n/l.dart | 4 + lib/src/main/main_bloc.dart | 4 + lib/src/message/message_attachment_bloc.dart | 5 +- .../display_notification_manager.dart | 182 +++++++++++ .../local_notification_manager.dart | 10 +- .../notifications/notification_manager.dart | 211 ------------- lib/src/push/push_bloc.dart | 285 ++++++++++-------- lib/src/push/push_event_state.dart | 12 - lib/src/push/push_manager.dart | 54 ++-- lib/src/push/push_service.dart | 2 +- lib/src/secure/generator.dart | 18 +- lib/src/settings/settings_debug_bloc.dart | 4 +- lib/src/share/share_bloc.dart | 5 +- lib/src/share/shared_data.dart | 1 - lib/src/utils/constants.dart | 6 + lib/src/utils/http.dart | 2 +- pubspec.lock | 11 +- pubspec.yaml | 4 +- test/push/push.dart | 11 +- 24 files changed, 429 insertions(+), 431 deletions(-) create mode 100644 lib/src/notifications/display_notification_manager.dart diff --git a/lib/src/chat/chat_bloc.dart b/lib/src/chat/chat_bloc.dart index d1dbe73d..63d6d8d8 100644 --- a/lib/src/chat/chat_bloc.dart +++ b/lib/src/chat/chat_bloc.dart @@ -52,7 +52,7 @@ import 'package:ox_coi/src/data/repository_stream_handler.dart'; import 'package:ox_coi/src/extensions/color_apis.dart'; import 'package:ox_coi/src/l10n/l.dart'; import 'package:ox_coi/src/l10n/l10n.dart'; -import 'package:ox_coi/src/notifications/notification_manager.dart'; +import 'package:ox_coi/src/notifications/display_notification_manager.dart'; class ChatBloc extends Bloc { final _chatRepository = RepositoryManager.get(RepositoryType.chat); @@ -207,7 +207,7 @@ class ChatBloc extends Bloc { } void _removeNotifications() { - final notificationManager = NotificationManager(); - notificationManager.cancelNotification(_chatId); + final notificationManager = DisplayNotificationManager(); + notificationManager.cancelNotificationAsync(_chatId); } } diff --git a/lib/src/contact/contact_change.dart b/lib/src/contact/contact_change.dart index 8e21d55b..6cd0f607 100644 --- a/lib/src/contact/contact_change.dart +++ b/lib/src/contact/contact_change.dart @@ -40,7 +40,7 @@ * for more details. */ -import 'package:delta_chat_core/delta_chat_core.dart' as Core; +import 'package:delta_chat_core/delta_chat_core.dart' as dcc; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -102,7 +102,7 @@ class _ContactChangeState extends State { ContactItemBloc _contactItemBloc = ContactItemBloc(); - Repository chatRepository; + Repository chatRepository; @override void initState() { diff --git a/lib/src/extensions/numbers_apis.dart b/lib/src/extensions/numbers_apis.dart index fc4b922c..22daabab 100644 --- a/lib/src/extensions/numbers_apis.dart +++ b/lib/src/extensions/numbers_apis.dart @@ -2,10 +2,10 @@ import 'package:date_format/date_format.dart'; import 'package:ox_coi/src/l10n/l.dart'; import 'package:ox_coi/src/l10n/l10n.dart'; -const _kilobyte = 1024; -const _megabyte = 1024 * _kilobyte; - extension Convert on int { + static const _kilobyte = 1024; + static const _megabyte = 1024 * _kilobyte; + String byteToPrintableSize() { String unit; double result; diff --git a/lib/src/invite/invite_bloc.dart b/lib/src/invite/invite_bloc.dart index b2e4c665..467ece9f 100644 --- a/lib/src/invite/invite_bloc.dart +++ b/lib/src/invite/invite_bloc.dart @@ -59,6 +59,7 @@ import 'package:ox_coi/src/invite/invite_service.dart'; import 'package:ox_coi/src/l10n/l.dart'; import 'package:ox_coi/src/l10n/l10n.dart'; import 'package:ox_coi/src/share/shared_data.dart'; +import 'package:ox_coi/src/utils/constants.dart'; import 'package:ox_coi/src/utils/http.dart'; import 'package:ox_coi/src/utils/image.dart'; import 'package:path_provider/path_provider.dart'; @@ -68,7 +69,7 @@ import 'invite_event_state.dart'; class InviteBloc extends Bloc { final Repository _contactRepository = RepositoryManager.get(RepositoryType.contact); final Repository _chatRepository = RepositoryManager.get(RepositoryType.chat); - static const platform = const MethodChannel(SharedData.sharingChannelName); + static const sharingChannel = const MethodChannel(kMethodChannelSharing); InviteService inviteService = InviteService(); @override @@ -92,7 +93,7 @@ class InviteBloc extends Bloc { Stream createInviteUrl(String message) async* { InviteServiceRequest requestInviteService = await _createInviteServiceRequest(message ?? ""); var response = await inviteService.createInviteUrl(requestInviteService); - bool valid = validateHttpResponse(response); + bool valid = isHttpResponseValid(response); if (valid) { InviteServiceResponse responseInviteService = _getInviteResponse(response); Map argsMap = { @@ -117,7 +118,7 @@ class InviteBloc extends Bloc { String id = sharedLink.substring(startIndex); if (id.isNotEmpty) { Response response = await inviteService.getInvite(id); - bool valid = validateHttpResponse(response); + bool valid = isHttpResponseValid(response); if (valid) { InviteServiceResponse responseInviteService = _getInviteResponse(response); String imageString = responseInviteService.sender.image; @@ -204,7 +205,7 @@ class InviteBloc extends Bloc { return inviteResponse; } - Future _getInitialLink() async => await platform.invokeMethod('getInitialLink'); + Future _getInitialLink() async => await sharingChannel.invokeMethod('getInitialLink'); - void sendSharedData(Map argsMap) async => await platform.invokeMethod('sendSharedData', argsMap); + void sendSharedData(Map argsMap) async => await sharingChannel.invokeMethod('sendSharedData', argsMap); } diff --git a/lib/src/invite/invite_service.dart b/lib/src/invite/invite_service.dart index 83037df1..7edc09bd 100644 --- a/lib/src/invite/invite_service.dart +++ b/lib/src/invite/invite_service.dart @@ -54,7 +54,7 @@ import 'package:ox_coi/src/utils/http.dart'; class InviteService { static InviteService _instance; - var _logger = Logger("invite_service"); + final _logger = Logger("invite_service"); var headers = {"Content-type": "application/json"}; factory InviteService() => _instance ??= InviteService._internal(); diff --git a/lib/src/l10n/l.dart b/lib/src/l10n/l.dart index 4ee476a7..77c1d239 100644 --- a/lib/src/l10n/l.dart +++ b/lib/src/l10n/l.dart @@ -456,6 +456,10 @@ class L { static final settingsAppearanceDescription = translationKey("Here you can choose your favorite theme. If you choose '%s', the theme may change automatically. This depends on whether you have selected 'Automatic' in the system preferences or not."); static final settingsAppearanceSystemThemeDescription = translationKey("Current System theme is: %s"); + static final notificationChannelTitle = _translationKey("Message notifications"); + static final notificationChannelDescription = _translationKey("Notifications for incoming messages"); + + static List _translationKey(String key, [String pluralKey]) { static final dynamicScreenSkipButtonTitle = L.translationKey("Skip"); static final dynamicScreenBackButtonTitle = L.translationKey("Back"); static final dynamicScreenNextButtonTitle = L.translationKey("Next"); diff --git a/lib/src/main/main_bloc.dart b/lib/src/main/main_bloc.dart index 6955e987..6e99dd22 100644 --- a/lib/src/main/main_bloc.dart +++ b/lib/src/main/main_bloc.dart @@ -63,6 +63,7 @@ import 'package:ox_coi/src/l10n/l.dart'; import 'package:ox_coi/src/l10n/l10n.dart'; import 'package:ox_coi/src/main/main_event_state.dart'; import 'package:ox_coi/src/notifications/local_notification_manager.dart'; +import 'package:ox_coi/src/notifications/display_notification_manager.dart'; import 'package:ox_coi/src/platform/app_information.dart'; import 'package:ox_coi/src/platform/preferences.dart'; import 'package:ox_coi/src/push/push_bloc.dart'; @@ -74,6 +75,9 @@ import 'package:permission_handler/permission_handler.dart'; class MainBloc extends Bloc { final _logger = Logger("main_bloc"); + final _notificationManager = DisplayNotificationManager(); + final _pushManager = PushManager(); + final _localNotificationManager = LocalNotificationManager(); Config _config = Config(); Context _context = Context(); diff --git a/lib/src/message/message_attachment_bloc.dart b/lib/src/message/message_attachment_bloc.dart index e3282a43..9d890eae 100644 --- a/lib/src/message/message_attachment_bloc.dart +++ b/lib/src/message/message_attachment_bloc.dart @@ -54,14 +54,13 @@ import 'package:ox_coi/src/data/repository.dart'; import 'package:ox_coi/src/data/repository_manager.dart'; import 'package:ox_coi/src/extensions/numbers_apis.dart'; import 'package:ox_coi/src/message/message_attachment_event_state.dart'; -import 'package:ox_coi/src/share/shared_data.dart'; import 'package:ox_coi/src/utils/constants.dart'; import 'package:ox_coi/src/utils/video.dart'; import 'package:path/path.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; class MessageAttachmentBloc extends Bloc { - static const platform = const MethodChannel(SharedData.sharingChannelName); + static const sharingChannel = const MethodChannel(kMethodChannelSharing); final _logger = Logger("message_attachment_bloc"); Repository _messageListRepository; @@ -110,7 +109,7 @@ class MessageAttachmentBloc extends Bloc{'title': '$text', 'path': '$filePath', 'mimeType': '$mime', 'text': '$text'}; - await platform.invokeMethod('sendSharedData', argsMap); + await sharingChannel.invokeMethod('sendSharedData', argsMap); } Core.ChatMsg _getMessage(int messageId) { diff --git a/lib/src/notifications/display_notification_manager.dart b/lib/src/notifications/display_notification_manager.dart new file mode 100644 index 00000000..5af1c8dc --- /dev/null +++ b/lib/src/notifications/display_notification_manager.dart @@ -0,0 +1,182 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import 'package:delta_chat_core/delta_chat_core.dart' as dcc; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:ox_coi/src/chat/chat.dart'; +import 'package:ox_coi/src/data/repository_manager.dart'; +import 'package:ox_coi/src/extensions/string_apis.dart'; +import 'package:ox_coi/src/l10n/l.dart'; +import 'package:ox_coi/src/l10n/l10n.dart'; +import 'package:ox_coi/src/lifecycle/lifecycle_bloc.dart'; +import 'package:ox_coi/src/navigation/navigatable.dart'; +import 'package:ox_coi/src/navigation/navigation.dart'; +import 'package:ox_coi/src/utils/constants.dart'; + + + +class DisplayNotificationManager { + static const _androidIconPath = '@mipmap/ic_notification'; + static const _payloadIdSeparator = "_"; + static const _payloadChatIdPosition = 0; + static const _payloadMessageIdPosition = 1; + + final _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + + final platformChannelSpecifics = NotificationDetails( + AndroidNotificationDetails( + kNotificationChannelMainId, + L10n.get(L.notificationChannelTitle), + L10n.get(L.notificationChannelDescription), + importance: Importance.Max, + priority: Priority.High, + ), + IOSNotificationDetails(), + ); + + static DisplayNotificationManager _instance; + + BuildContext _buildContext; + + factory DisplayNotificationManager() => _instance ??= new DisplayNotificationManager._internal(); + + DisplayNotificationManager._internal(); + + Future setupAsync(BuildContext buildContext) async { + this._buildContext = buildContext; + final initializationSettingsAndroid = AndroidInitializationSettings(_androidIconPath); + final initializationSettingsIOS = IOSInitializationSettings(); + final initializationSettings = InitializationSettings(initializationSettingsAndroid, initializationSettingsIOS); + await _flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: onSelectNotification); + } + + Future onSelectNotification(String payload) { + Navigation navigation = Navigation(); + if (!payload.isNullOrEmpty()) { + int chatId = getIdFromPayload(payload, _payloadChatIdPosition); + int messageId = getIdFromPayload(payload, _payloadMessageIdPosition); + var isChatOpened = navigation.current?.equal(Navigatable(Type.chat, params: [chatId, messageId])); + if (isChatOpened == null || !isChatOpened) { + navigation.pushAndRemoveUntil( + _buildContext, + MaterialPageRoute( + builder: (context) { + return Chat( + chatId: chatId, + messageId: messageId, + headlessStart: true, + ); + }, + ), + ModalRoute.withName(Navigation.root), + Navigatable(Type.rootChildren), + ); + } else { + navigation.popUntilRoot(_buildContext); + } + } else { + navigation.popUntilRoot(_buildContext); + } + return Future.value(true); + } + + int getIdFromPayload(String payload, int idPosition) { + var hasSeparator = payload.contains(_payloadIdSeparator); + if (!hasSeparator && idPosition > _payloadChatIdPosition) { + return null; + } + String idString = hasSeparator ? payload.split(_payloadIdSeparator)[idPosition] : payload; + return idString != null ? int.parse(idString) : null; + } + + Future showNotificationFromPushAsync(String fromEmail, String body) async { + if (_buildContext != null && _isAppResumed()) { + return; + } + final contactRepository = RepositoryManager.get(RepositoryType.contact); + String name = fromEmail; + int chatId; + await Future.forEach(contactRepository.getAll(), (dcc.Contact contact) async { + final address = await contact.getAddress(); + if (address == fromEmail) { + final contactName = await contact.getName(); + name = contactName.isNotEmpty ? contactName : fromEmail; + final context = dcc.Context(); + chatId = await context.getChatByContactId(contact.id); + } + }); + await _flutterLocalNotificationsPlugin.show(chatId, name, body, platformChannelSpecifics, payload: chatId != 0 ? chatId.toString() : null); + } + + Future showNotificationFromLocalAsync(int chatId, String title, String body, {String payload}) async { + if (_buildContext != null) { + final navigation = Navigation(); + if (_isAppInForeground(navigation)) { + final isChatOpened = navigation.current.equal(Navigatable(Type.chat, params: [chatId])); + final isChatListOpened = navigation.current.equal(Navigatable(Type.chatList)); + if (isChatOpened || isChatListOpened) { + return; + } + } + } + await _flutterLocalNotificationsPlugin.show(chatId, title, body, platformChannelSpecifics, payload: payload); + } + + bool _isAppInForeground(Navigation navigation) => navigation.hasElements() && _isAppResumed(); + + bool _isAppResumed() => BlocProvider.of(_buildContext).currentBackgroundState == AppLifecycleState.resumed.toString(); + + Future isAppLaunchedFromNotificationAsync() async { + final notificationAppLaunchDetails = await _flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); + return notificationAppLaunchDetails.didNotificationLaunchApp; + } + + Future cancelNotificationAsync(int id) async { + await _flutterLocalNotificationsPlugin.cancel(id); + } + + Future cancelAllNotificationsAsync() async { + await _flutterLocalNotificationsPlugin.cancelAll(); + } +} diff --git a/lib/src/notifications/local_notification_manager.dart b/lib/src/notifications/local_notification_manager.dart index 0e021da5..4ecb635c 100644 --- a/lib/src/notifications/local_notification_manager.dart +++ b/lib/src/notifications/local_notification_manager.dart @@ -49,7 +49,7 @@ import 'package:ox_coi/src/data/chat_message_repository.dart'; import 'package:ox_coi/src/data/repository_manager.dart'; import 'package:ox_coi/src/l10n/l.dart'; import 'package:ox_coi/src/l10n/l10n.dart'; -import 'package:ox_coi/src/notifications/notification_manager.dart'; +import 'package:ox_coi/src/notifications/display_notification_manager.dart'; import 'package:ox_coi/src/platform/preferences.dart'; import 'package:rxdart/rxdart.dart'; @@ -64,7 +64,7 @@ class LocalNotificationManager { final _core = DeltaChatCore(); final _context = Context(); - NotificationManager _notificationManager; + DisplayNotificationManager _notificationManager; bool _listenersRegistered = false; factory LocalNotificationManager() => _instance ??= LocalNotificationManager._internal(); @@ -82,7 +82,7 @@ class LocalNotificationManager { void _registerListeners() { if (!_listenersRegistered) { _listenersRegistered = true; - _notificationManager = NotificationManager(); + _notificationManager = DisplayNotificationManager(); _messageSubject.listen(_messagesUpdated); _core.addListener(eventIdList: [Event.incomingMsg, Event.msgsChanged], streamController: _messageSubject); } @@ -129,7 +129,7 @@ class LocalNotificationManager { final teaser = await message.getSummaryText(200); final payload = chatId?.toString(); _logger.info("Creating chat notification for chat id $chatId with message id $messageId"); - _notificationManager.showNotificationFromLocal(chatId, title, teaser, payload: payload); + _notificationManager.showNotificationFromLocalAsync(chatId, title, teaser, payload: payload); } } }); @@ -185,7 +185,7 @@ class LocalNotificationManager { final teaser = await message.getSummaryText(200); final payload = "${Chat.typeInvite.toString()}_$messageId"; _logger.info("Creating invite notification for sender id $senderId with message id $messageId"); - _notificationManager.showNotificationFromLocal(Chat.typeInvite, title, teaser, payload: payload); + _notificationManager.showNotificationFromLocalAsync(Chat.typeInvite, title, teaser, payload: payload); } }); diff --git a/lib/src/notifications/notification_manager.dart b/lib/src/notifications/notification_manager.dart index dd5b1dd4..e69de29b 100644 --- a/lib/src/notifications/notification_manager.dart +++ b/lib/src/notifications/notification_manager.dart @@ -1,211 +0,0 @@ -/* - * OPEN-XCHANGE legal information - * - * All intellectual property rights in the Software are protected by - * international copyright laws. - * - * - * In some countries OX, OX Open-Xchange and open xchange - * as well as the corresponding Logos OX Open-Xchange and OX are registered - * trademarks of the OX Software GmbH group of companies. - * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). - * Instead, you are allowed to use these Logos according to the terms and - * conditions of the Creative Commons License, Version 2.5, Attribution, - * Non-commercial, ShareAlike, and the interpretation of the term - * Non-commercial applicable to the aforementioned license is published - * on the web site https://www.open-xchange.com/terms-and-conditions/. - * - * Please make sure that third-party modules and libraries are used - * according to their respective licenses. - * - * Any modifications to this package must retain all copyright notices - * of the original copyright holder(s) for the original code used. - * - * After any such modifications, the original and derivative code shall remain - * under the copyright of the copyright holder(s) and/or original author(s) as stated here: - * https://www.open-xchange.com/legal/. The contributing author shall be - * given Attribution for the derivative code and a license granting use. - * - * Copyright (C) 2016-2020 OX Software GmbH - * Mail: info@open-xchange.com - * - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 - * for more details. - */ - -import 'package:delta_chat_core/delta_chat_core.dart' as DeltaChatCore; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:ox_coi/src/chat/chat.dart'; -import 'package:ox_coi/src/data/repository.dart'; -import 'package:ox_coi/src/data/repository_manager.dart'; -import 'package:ox_coi/src/extensions/string_apis.dart'; -import 'package:ox_coi/src/lifecycle/lifecycle_bloc.dart'; -import 'package:ox_coi/src/navigation/navigatable.dart'; -import 'package:ox_coi/src/navigation/navigation.dart'; - -class NotificationManager { - static const idSeparator = "_"; - static const chatIdPosition = 0; - static const messageIdPosition = 1; - - final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - - - //TODO: Add better AndroidNotificationDetails - final platformChannelSpecifics = NotificationDetails( - AndroidNotificationDetails( - 'com.android.oxcoi.notification.single', - 'Message notification', - 'Notification for incoming messages', - importance: Importance.Max, - priority: Priority.High, - ), - IOSNotificationDetails(), - ); - - static NotificationManager _instance; - - BuildContext _buildContext; - - factory NotificationManager() => _instance ??= new NotificationManager._internal(); - - NotificationManager._internal(); - - void setup(BuildContext buildContext) { - this._buildContext = buildContext; - //localNotification setup - var initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_notification'); - var initializationSettingsIOS = IOSInitializationSettings(onDidReceiveLocalNotification: onDidReceiveLocalNotification); - var initializationSettings = InitializationSettings(initializationSettingsAndroid, initializationSettingsIOS); - _flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: onSelectNotification); - } - - Future onDidReceiveLocalNotification(int id, String title, String body, String payload) { - //TODO: Use payload to navigate to the right location/chat - debugPrint("NotificationManager.onDidRecieveLocalNotification() payload = $payload"); - return Future.value(false); - } - - Future onSelectNotification(String payload) { - Navigation navigation = Navigation(); - if (!payload.isNullOrEmpty()) { - int chatId = getIdFromPayload(payload, chatIdPosition); - int messageId = getIdFromPayload(payload, messageIdPosition); - var isChatOpened = navigation.current?.equal(Navigatable(Type.chat, params: [chatId, messageId])); - if (isChatOpened == null || !isChatOpened) { - navigation.pushAndRemoveUntil( - _buildContext, - MaterialPageRoute( - builder: (context) { - return Chat( - chatId: chatId, - messageId: messageId, - headlessStart: true, - ); - }, - ), - ModalRoute.withName(Navigation.root), - Navigatable(Type.rootChildren), - ); - } else { - navigation.popUntilRoot(_buildContext); - } - } else { - navigation.popUntilRoot(_buildContext); - } - return Future.value(true); - } - - int getIdFromPayload(String payload, int idPosition) { - var hasSeparator = payload.contains(idSeparator); - if (!hasSeparator && idPosition > chatIdPosition) { - return null; - } - String idString = hasSeparator ? payload.split(idSeparator)[idPosition] : payload; - return idString != null ? int.parse(idString) : null; - } - - Future showNotificationFromPush(String fromEmail, String body) async { - if (_buildContext != null) { - // Ignoring false positive https://github.com/felangel/bloc/issues/587 - // ignore: close_sinks - var lifecycleBloc = BlocProvider.of(_buildContext); - if (lifecycleBloc.currentBackgroundState == AppLifecycleState.resumed.toString()) { - return; - } - } - Repository _contactRepository = RepositoryManager.get(RepositoryType.contact); - String name = fromEmail; - int chatId; - await Future.forEach(_contactRepository.getAll(), (DeltaChatCore.Contact contact) async { - String address = await contact.getAddress(); - if (address == fromEmail) { - var contactName = await contact.getName(); - name = contactName.isNotEmpty ? contactName : fromEmail; - var context = DeltaChatCore.Context(); - chatId = await context.getChatByContactId(contact.id); - } - }); - await _flutterLocalNotificationsPlugin.show(chatId, name, body, platformChannelSpecifics, payload: chatId != 0 ? chatId.toString() : null); - } - - Future showNotificationFromLocal(int chatId, String title, String body, {String payload}) async { - if (_buildContext != null) { - // Ignoring false positive https://github.com/felangel/bloc/issues/587 - // ignore: close_sinks - var lifecycleBloc = BlocProvider.of(_buildContext); - var navigation = Navigation(); - if (navigation.hasElements() && lifecycleBloc?.currentBackgroundState == AppLifecycleState.resumed.toString()) { - var isChatOpened = navigation.current.equal(Navigatable(Type.chat, params: [chatId])); - var isChatListOpened = navigation.current.equal(Navigatable(Type.chatList)); - if (isChatOpened || isChatListOpened) { - return; - } - } - } - await _flutterLocalNotificationsPlugin.show(chatId, title, body, platformChannelSpecifics, payload: payload); - } - - //show group notification (Android only) - Future showGroupNotification(int chatId, String title, String body, {String payload}) async { - //TODO: Add better names - String groupKey = 'com.android.oxcoi.WORK_EMAIL'; - String groupChannelId = 'com.android.oxcoi.notification.group'; - String groupChannelName = 'Group notification'; - String groupChannelDescription = 'Notification for grouped messages'; - - AndroidNotificationDetails androidNotificationSpecifics = new AndroidNotificationDetails( - groupChannelId, groupChannelName, groupChannelDescription, - importance: Importance.Max, priority: Priority.High, groupKey: groupKey); - NotificationDetails notificationPlatformSpecifics = new NotificationDetails(androidNotificationSpecifics, null); - await _flutterLocalNotificationsPlugin.show(chatId, title, body, notificationPlatformSpecifics, payload: payload); - - AndroidNotificationDetails androidSummaryNotificationSpecifics = new AndroidNotificationDetails( - groupChannelId, groupChannelName, groupChannelDescription, - importance: Importance.Max, priority: Priority.High, groupKey: groupKey, setAsGroupSummary: true); - NotificationDetails notificationSummaryPlatformSpecifics = new NotificationDetails(androidSummaryNotificationSpecifics, null); - await _flutterLocalNotificationsPlugin.show(chatId, "", "", notificationSummaryPlatformSpecifics, payload: payload); - } - - Future isAppLaunchedFromNotification() async { - var notificationAppLaunchDetails = await _flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); - return notificationAppLaunchDetails.didNotificationLaunchApp; - } - - Future cancelNotification(int id) async { - await _flutterLocalNotificationsPlugin.cancel(id); - } - - Future cancelAllNotifications() async { - await _flutterLocalNotificationsPlugin.cancelAll(); - } -} diff --git a/lib/src/push/push_bloc.dart b/lib/src/push/push_bloc.dart index af430303..feeb7fa2 100644 --- a/lib/src/push/push_bloc.dart +++ b/lib/src/push/push_bloc.dart @@ -56,6 +56,7 @@ import 'package:ox_coi/src/platform/system_information.dart'; import 'package:ox_coi/src/push/push_event_state.dart'; import 'package:ox_coi/src/push/push_manager.dart'; import 'package:ox_coi/src/secure/generator.dart'; +import 'package:ox_coi/src/utils/constants.dart'; import 'package:ox_coi/src/utils/http.dart'; import 'package:rxdart/rxdart.dart'; @@ -72,111 +73,42 @@ enum PushSetupState { } class PushBloc extends Bloc { - var _logger = Logger("push_bloc"); - var _pushManager = PushManager(); - var pushService = PushService(); - var _core = DeltaChatCore(); - var _context = Context(); - bool _listenersRegistered = false; static const _subscribeListenerId = 1001; static const _validateListenerId = 1002; + static const _securityChannel = const MethodChannel(kMethodChannelSecurity); + + final _logger = Logger("push_bloc"); + final _pushManager = PushManager(); + final pushService = PushService(); + final _core = DeltaChatCore(); + final _context = Context(); + final _pushSubject = PublishSubject(); - PublishSubject _pushSubject = new PublishSubject(); - static const securityChannelName = const MethodChannel("oxcoi.security"); + bool _listenersRegistered = false; @override PushState get initialState => PushStateInitial(); @override Stream mapEventToState(PushEvent event) async* { - bool pushAvailable = await _isWebPushAvailable(); + final pushAvailable = await _isWebPushAvailableAsync(); if (!pushAvailable) { yield PushStateSuccess(pushAvailable: false, pushSetupState: PushSetupState.initial); - _setNotificationPushStatus(PushSetupState.initial); + _setNotificationPushStatusAsync(PushSetupState.initial); return; } if (event is RegisterPushResource) { - try { - RequestPushRegistration requestPushRegistration = await _createRegistrationRequest(); - var response = await pushService.registerPush(requestPushRegistration); - var valid = validateHttpResponse(response); - if (valid) { - ResponsePushResource pushResource = _getPushResource(response); - await _persistPushResource(pushResource); - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.resourceRegistered, - ); - _setNotificationPushStatus(PushSetupState.resourceRegistered); - add(SubscribeMetadata(pushResource: pushResource)); - } - } catch (error) { - yield PushStateFailure(error: error.toString()); - } + yield* _registerPushResource(); } else if (event is GetPushResource) { - try { - String id = await _getId(); - var response = await pushService.getPush(id); - var valid = validateHttpResponse(response); - if (valid) { - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.unchanged, - ); - } - } catch (error) { - yield PushStateFailure(error: error.toString()); - } + yield* _getPushResource(); } else if (event is PatchPushResource) { - try { - String id = await _getId(); - RequestPushPatch requestPushPatch = _createPatchRequest(event.pushToken); - var response = await pushService.patchPush(id, requestPushPatch); - var valid = validateHttpResponse(response); - if (valid) { - ResponsePushResource pushResource = _getPushResource(response); - await _persistPushResource(pushResource); - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.resourceRegistered, - ); - _setNotificationPushStatus(PushSetupState.resourceRegistered); - // TODO is requesting a new metadata subscription actually needed? Does the push resource ID or endpoint changes? - add(SubscribeMetadata(pushResource: pushResource)); - } - } catch (error) { - yield PushStateFailure(error: error.toString()); - } + yield* _patchPushResource(event); } else if (event is DeletePushResource) { - try { - String id = await _getId(); - var response = await pushService.deletePush(id); - var valid = validateHttpResponse(response); - if (valid) { - _removePushResource(); - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.initial, - ); - _setNotificationPushStatus(PushSetupState.initial); - } - } catch (error) { - yield PushStateFailure(error: error.toString()); - } + yield* _deletePushResource(); } else if (event is SubscribeMetadata) { - await _subscribeMetaData(event.pushResource); - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.sendMetadataSubscribe, - ); - _setNotificationPushStatus(PushSetupState.sendMetadataSubscribe); + yield* _subscribeMetadata(event); } else if (event is ValidateMetadata) { - await _confirmValidation(event.validation); - yield PushStateSuccess( - pushAvailable: true, - pushSetupState: PushSetupState.sendMetadataValidate, - ); - _setNotificationPushStatus(PushSetupState.sendMetadataValidate); + yield* _validateMetadata(event); } } @@ -186,67 +118,153 @@ class PushBloc extends Bloc { return super.close(); } - Future _createRegistrationRequest() async { - var appId = await getPackageName(); - var pushToken = await _pushManager.getPushToken(); - var publicKey = await _getCoiServerPublicKey(); - publicKey = publicKey.replaceAll("\n", ""); - var requestPushRegistration = RequestPushRegistration(appId, publicKey, pushToken); - return requestPushRegistration; + Stream _registerPushResource() async* { + try { + final requestPushRegistration = await _createRegistrationRequestAsync(); + final response = await pushService.registerPush(requestPushRegistration); + final valid = isHttpResponseValid(response); + if (valid) { + final pushResource = _createPushResource(response); + await _persistPushResourceAsync(pushResource); + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.resourceRegistered, + ); + _setNotificationPushStatusAsync(PushSetupState.resourceRegistered); + add(SubscribeMetadata(pushResource: pushResource)); + } + } catch (error) { + yield PushStateFailure(error: error.toString()); + } + } + + Stream _getPushResource() async* { + try { + final id = await _getIdAsync(); + final response = await pushService.getPush(id); + final valid = isHttpResponseValid(response); + if (valid) { + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.unchanged, + ); + } + } catch (error) { + yield PushStateFailure(error: error.toString()); + } + } + + Stream _patchPushResource(PatchPushResource event) async* { + try { + final id = await _getIdAsync(); + final requestPushPatch = _createPatchRequest(event.pushToken); + final response = await pushService.patchPush(id, requestPushPatch); + final valid = isHttpResponseValid(response); + if (valid) { + final pushResource = _createPushResource(response); + await _persistPushResourceAsync(pushResource); + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.resourceRegistered, + ); + _setNotificationPushStatusAsync(PushSetupState.resourceRegistered); + add(SubscribeMetadata(pushResource: pushResource)); + } + } catch (error) { + yield PushStateFailure(error: error.toString()); + } + } + + Stream _deletePushResource() async* { + try { + final id = await _getIdAsync(); + final response = await pushService.deletePush(id); + final valid = isHttpResponseValid(response); + if (valid) { + _removePushResourceAsync(); + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.initial, + ); + _setNotificationPushStatusAsync(PushSetupState.initial); + } + } catch (error) { + yield PushStateFailure(error: error.toString()); + } } - RequestPushPatch _createPatchRequest(String pushToken) { - var requestPushRegistration = RequestPushPatch(pushToken); - return requestPushRegistration; + Stream _subscribeMetadata(SubscribeMetadata event) async* { + await _subscribeMetaDataAsync(event.pushResource); + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.sendMetadataSubscribe, + ); + _setNotificationPushStatusAsync(PushSetupState.sendMetadataSubscribe); } - Future _getId() async { - var pushResourceJsonString = await getPreference(preferenceNotificationsPush); - var pushResourceJsonMap = jsonDecode(pushResourceJsonString); - var pushResource = ResponsePushResource.fromJson(pushResourceJsonMap); + Stream _validateMetadata(ValidateMetadata event) async* { + await _confirmValidationAsync(event.validation); + yield PushStateSuccess( + pushAvailable: true, + pushSetupState: PushSetupState.sendMetadataValidate, + ); + _setNotificationPushStatusAsync(PushSetupState.sendMetadataValidate); + } + + Future _createRegistrationRequestAsync() async { + final appId = await getPackageName(); + final pushToken = await _pushManager.getPushTokenAsync(); + var publicKey = await _getCoiServerPublicKeyAsync(); + publicKey = publicKey.replaceAll("\n", ""); + return RequestPushRegistration(appId, publicKey, pushToken); + } + + RequestPushPatch _createPatchRequest(String pushToken) => RequestPushPatch(pushToken); + + Future _getIdAsync() async { + final pushResourceJsonString = await getPreference(preferenceNotificationsPush); + final pushResourceJsonMap = jsonDecode(pushResourceJsonString); + final pushResource = ResponsePushResource.fromJson(pushResourceJsonMap); return pushResource.id; } - ResponsePushResource _getPushResource(Response response) { + ResponsePushResource _createPushResource(Response response) { var pushResource; if (response.body != null && response.body.isNotEmpty) { - Map responseMap = jsonDecode(response.body); + final responseMap = jsonDecode(response.body); pushResource = ResponsePushResource.fromJson(responseMap); } return pushResource; } - Future _persistPushResource(ResponsePushResource pushResource) async { + Future _persistPushResourceAsync(ResponsePushResource pushResource) async { if (pushResource != null) { await setPreference(preferenceNotificationsPush, jsonEncode(pushResource)); } } - Future _removePushResource() async { + Future _removePushResourceAsync() async { await removePreference(preferenceNotificationsPush); } - Future _isWebPushAvailable() async { - _context = Context(); + Future _isWebPushAvailableAsync() async { return await _context.isWebPushSupported() == 1; } - Future _getCoiServerPublicKey() async { - _context = Context(); + Future _getCoiServerPublicKeyAsync() async { return await _context.getWebPushVapidKey(); } - Future _subscribeMetaData(ResponsePushResource responsePushResource) async { - await _generateSecrets(); - String publicKey = await _getKey(); - //TODO decide where to get / load / generate all keys, we should prefer Dart if possible - String auth = await _getAuthSecret(); - String clientEndpoint = generateUuid(); + Future _subscribeMetaDataAsync(ResponsePushResource responsePushResource) async { + await _generateSecretsAsync(); + final publicKey = await _getKeyAsync(); + final auth = await _getAuthSecretAsync(); + final clientEndpoint = generateUuid(); await setPreference(preferenceNotificationsEndpoint, clientEndpoint); - String client = await getAppName(); - String device = await getDeviceName(); - var pushSubscribeMetaData = PushSubscribeMetaData( + final client = await getAppName(); + final device = await getDeviceName(); + final pushSubscribeMetaData = PushSubscribeMetaData( client: client, device: device, resource: PushSubscribeMetaDataResource( @@ -258,20 +276,19 @@ class PushBloc extends Bloc { ), ); _registerListeners(); - _context = Context(); - String encodedBody = json.encode(pushSubscribeMetaData); + final encodedBody = json.encode(pushSubscribeMetaData); await _context.subscribeWebPush(clientEndpoint, encodedBody, _subscribeListenerId); } - Future _confirmValidation(String message) async { - String clientEndpoint = await getPreference(preferenceNotificationsEndpoint); + Future _confirmValidationAsync(String message) async { + final clientEndpoint = await getPreference(preferenceNotificationsEndpoint); await _context.validateWebPush(clientEndpoint, message, _validateListenerId); } - void _registerListeners() async { + void _registerListeners() { if (!_listenersRegistered) { _listenersRegistered = true; - _pushSubject.listen(_metadataSuccessCallback, onError: _errorCallback); + _pushSubject.listen(_metadataSuccessCallbackAsync, onError: _errorCallback); _core.addListener(eventIdList: [Event.setMetaDataDone, Event.webPushSubscription], streamController: _pushSubject); } } @@ -283,12 +300,12 @@ class PushBloc extends Bloc { } } - void _metadataSuccessCallback(Event event) { - var data1 = event.data1; - if (data1 == _subscribeListenerId) { - _setNotificationPushStatus(PushSetupState.metadataSubscribed); - } else if (data1 == _validateListenerId) { - _setNotificationPushStatus(PushSetupState.metadataValidated); + Future _metadataSuccessCallbackAsync(Event event) async { + final listenerId = event.data1; + if (listenerId == _subscribeListenerId) { + await _setNotificationPushStatusAsync(PushSetupState.metadataSubscribed); + } else if (listenerId == _validateListenerId) { + await _setNotificationPushStatusAsync(PushSetupState.metadataValidated); } } @@ -296,13 +313,13 @@ class PushBloc extends Bloc { _logger.info("An error occured while listening: $error"); } - _setNotificationPushStatus(PushSetupState state) { - setPreference(preferenceNotificationsPushStatus, describeEnum(state)); + Future _setNotificationPushStatusAsync(PushSetupState state) async { + await setPreference(preferenceNotificationsPushStatus, describeEnum(state)); } - Future _generateSecrets() async => await securityChannelName.invokeMethod('generateSecrets'); + Future _generateSecretsAsync() async => await _securityChannel.invokeMethod('generateSecrets'); // TODO move to dart - Future _getKey() async => await securityChannelName.invokeMethod('getKey'); + Future _getKeyAsync() async => await _securityChannel.invokeMethod('getKey'); // TODO move to dart - Future _getAuthSecret() async => await securityChannelName.invokeMethod('getAuthSecret'); + Future _getAuthSecretAsync() async => await _securityChannel.invokeMethod('getAuthSecret'); // TODO move to dart } diff --git a/lib/src/push/push_event_state.dart b/lib/src/push/push_event_state.dart index 0bfb465c..65e51b7b 100644 --- a/lib/src/push/push_event_state.dart +++ b/lib/src/push/push_event_state.dart @@ -70,18 +70,6 @@ class ValidateMetadata extends PushEvent { ValidateMetadata({@required this.validation}); } -class PushActionFailed extends PushEvent { - final String error; - - PushActionFailed({@required this.error}); -} - -class PushActionDone extends PushEvent { - final ResponsePushResource responsePushResource; - - PushActionDone({@required this.responsePushResource}); -} - abstract class PushState {} class PushStateInitial extends PushState {} diff --git a/lib/src/push/push_manager.dart b/lib/src/push/push_manager.dart index 473de451..a4249922 100644 --- a/lib/src/push/push_manager.dart +++ b/lib/src/push/push_manager.dart @@ -46,19 +46,25 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:logging/logging.dart'; import 'package:ox_coi/src/data/notification.dart'; import 'package:ox_coi/src/data/push_chat_message.dart'; import 'package:ox_coi/src/data/push_validation.dart'; import 'package:ox_coi/src/extensions/string_apis.dart'; -import 'package:ox_coi/src/notifications/notification_manager.dart'; +import 'package:ox_coi/src/notifications/display_notification_manager.dart'; import 'package:ox_coi/src/platform/preferences.dart'; import 'package:ox_coi/src/push/push_bloc.dart'; import 'package:ox_coi/src/push/push_event_state.dart'; +import 'package:ox_coi/src/utils/constants.dart'; class PushManager { - static const securityChannelName = const MethodChannel("oxcoi.security"); - FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); - var _notificationManager = NotificationManager(); + static const securityChannel = const MethodChannel(kMethodChannelSecurity); + + final _firebaseMessaging = FirebaseMessaging(); + final _notificationManager = DisplayNotificationManager(); + final _logger = Logger("push_manager"); + + BuildContext _buildContext; PushBloc _pushBloc; static PushManager _instance; @@ -72,65 +78,61 @@ class PushManager { //firebase setup _firebaseMessaging.configure( onMessage: (Map message) async { - print('on message $message'); - var notificationData = NotificationData.fromJson(message); + _logger.info(message); + final notificationData = NotificationData.fromJson(message); if (notificationData.valid) { - String decryptedContent = await decrypt(notificationData.content); + final decryptedContent = await decryptAsync(notificationData.content); if (_isValidationPush(decryptedContent)) { - var validation = _getPushValidation(decryptedContent).validation; + final validation = _getPushValidation(decryptedContent).validation; _pushBloc.add(ValidateMetadata(validation: validation)); } else { - var pushChatMessage = _getPushChatMessage(decryptedContent); - String fromEmail = pushChatMessage.fromEmail; - String body = "I sent you a new chat message"; - await _notificationManager.showNotificationFromPush(fromEmail, body); + final pushChatMessage = _getPushChatMessage(decryptedContent); + final fromEmail = pushChatMessage.fromEmail; + final body = "I sent you a new chat message"; // TODO replace decrypt + await _notificationManager.showNotificationFromPushAsync(fromEmail, body); } } return Future(null); }, onResume: (Map message) { //TODO: Add functionality - print('on resume $message'); + _logger.info("onResume $message"); return Future(null); }, onLaunch: (Map message) { //TODO: Add functionality - print('on launch $message'); + _logger.info("onLaunch $message"); return Future(null); }, ); _firebaseMessaging.requestNotificationPermissions(const IosNotificationSettings(sound: true, badge: true, alert: true)); - _firebaseMessaging.getToken().then((token) { - //TODO Use in production - //_pushBloc.add(PatchPushResource(pushToken: token)); - }); } - Future getPushToken() async { + Future getPushTokenAsync() async { return await _firebaseMessaging.getToken(); } - Future getPushResource() async { + Future getPushResourceAsync() async { return await getPreference(preferenceNotificationsPush); } - Future decrypt(String base64content) async { - return await securityChannelName.invokeMethod('decrypt', {"input": base64content}); + Future decryptAsync(String base64content) async { + return await securityChannel.invokeMethod('decrypt', {"input": base64content}); } bool _isValidationPush(String decryptedContent) { - var pushValidationMap = jsonDecode(decryptedContent); - var pushValidation = PushValidation.fromJson(pushValidationMap); + final pushValidationMap = jsonDecode(decryptedContent); + final pushValidation = PushValidation.fromJson(pushValidationMap); return !pushValidation.validation.isNullOrEmpty(); } PushValidation _getPushValidation(String decryptedContent) { - var pushValidationMap = jsonDecode(decryptedContent); + final pushValidationMap = jsonDecode(decryptedContent); return PushValidation.fromJson(pushValidationMap); } PushChatMessage _getPushChatMessage(String decryptedContent) { - var pushValidationMap = jsonDecode(decryptedContent); + final pushValidationMap = jsonDecode(decryptedContent); return PushChatMessage.fromJson(pushValidationMap); } } diff --git a/lib/src/push/push_service.dart b/lib/src/push/push_service.dart index 46862dfe..85f0e779 100644 --- a/lib/src/push/push_service.dart +++ b/lib/src/push/push_service.dart @@ -54,7 +54,7 @@ import 'package:ox_coi/src/utils/http.dart'; class PushService { static PushService _instance; - var _logger = Logger("push_service"); + final _logger = Logger("push_service"); var headers = {"Content-type": "application/json"}; factory PushService() => _instance ??= PushService._internal(); diff --git a/lib/src/secure/generator.dart b/lib/src/secure/generator.dart index f51d2b21..21211f00 100644 --- a/lib/src/secure/generator.dart +++ b/lib/src/secure/generator.dart @@ -52,12 +52,9 @@ import 'package:pointycastle/key_generators/ec_key_generator.dart'; import 'package:pointycastle/random/fortuna_random.dart'; import 'package:uuid/uuid.dart'; -AsymmetricKeyPair generateEcKeyPair() { - var domainParameters = ECCurve_secp256r1(); - var params = ECKeyGeneratorParameters(domainParameters); - var generator = ECKeyGenerator(); - generator.init(ParametersWithRandom(params, _getSecureRandom())); - return generator.generateKeyPair(); +String generateUuid() { + var uuid = new Uuid(); + return uuid.v4(); } String getPublicEcKey(AsymmetricKeyPair keyPair) { @@ -71,9 +68,12 @@ String getPrivateEcKey(AsymmetricKeyPair keyPair) { return privateKey.d.toString(); } -String generateUuid() { - var uuid = new Uuid(); - return uuid.v4(); +AsymmetricKeyPair generateEcKeyPair() { + var domainParameters = ECCurve_secp256r1(); + var params = ECKeyGeneratorParameters(domainParameters); + var generator = ECKeyGenerator(); + generator.init(ParametersWithRandom(params, _getSecureRandom())); + return generator.generateKeyPair(); } SecureRandom _getSecureRandom() { diff --git a/lib/src/settings/settings_debug_bloc.dart b/lib/src/settings/settings_debug_bloc.dart index 8504d264..7ad901e7 100644 --- a/lib/src/settings/settings_debug_bloc.dart +++ b/lib/src/settings/settings_debug_bloc.dart @@ -72,8 +72,8 @@ class SettingsDebugBloc extends Bloc { void loadDebug() async { var pushManager = PushManager(); - String token = await pushManager.getPushToken(); - String pushResource = await pushManager.getPushResource(); + String token = await pushManager.getPushTokenAsync(); + String pushResource = await pushManager.getPushResourceAsync(); String endpoint = await getPreference(preferenceNotificationsEndpoint); String pushServiceUrl = await getPreference(preferenceNotificationsPushServiceUrl); String pushState = await getPreference(preferenceNotificationsPushStatus); diff --git a/lib/src/share/share_bloc.dart b/lib/src/share/share_bloc.dart index 2358cdc3..21e4df2f 100644 --- a/lib/src/share/share_bloc.dart +++ b/lib/src/share/share_bloc.dart @@ -50,11 +50,12 @@ import 'package:ox_coi/src/contact/contact_list_event_state.dart'; import 'package:ox_coi/src/data/contact_repository.dart'; import 'package:ox_coi/src/share/share_event_state.dart'; import 'package:ox_coi/src/share/shared_data.dart'; +import 'package:ox_coi/src/utils/constants.dart'; class ShareBloc extends Bloc { ChatListBloc _chatListBloc = ChatListBloc(); ContactListBloc _contactListBloc = ContactListBloc(); - static const platform = const MethodChannel(SharedData.sharingChannelName); + static const sharingChannel = const MethodChannel(kMethodChannelSharing); @override ShareState get initialState => ShareStateInitial(); @@ -138,5 +139,5 @@ class ShareBloc extends Bloc { } } - Future _getSharedData() async => await platform.invokeMethod('getSharedData'); + Future _getSharedData() async => await sharingChannel.invokeMethod('getSharedData'); } diff --git a/lib/src/share/shared_data.dart b/lib/src/share/shared_data.dart index 896e9f9e..e7bcfafd 100644 --- a/lib/src/share/shared_data.dart +++ b/lib/src/share/shared_data.dart @@ -45,7 +45,6 @@ class SharedData { static const String sharedText = "shared_text"; static const String sharedPath = "shared_path"; static const String sharedFileName = "shared_file_name"; - static const String sharingChannelName = "oxcoi.intent"; String mimeType; String text; diff --git a/lib/src/utils/constants.dart b/lib/src/utils/constants.dart index ac58ea38..54b36139 100644 --- a/lib/src/utils/constants.dart +++ b/lib/src/utils/constants.dart @@ -53,6 +53,10 @@ const maxAttachmentSize = 100 * 1024 * 1024; // Means 100 MB // Extension database - the file is placed in the apps folder structure under ~/databases/$extensionDbName const extensionDbName = "extension.db"; +// Method channels +const kMethodChannelSecurity = 'oxcoi.security'; +const kMethodChannelSharing = 'oxcoi.sharing'; + // External services const defaultCoiPushServiceUrl = "https://push.coi.me/push/resource/"; const defaultCoiInviteServiceUrl = "https://invite.coi.me/invite/"; @@ -65,6 +69,8 @@ const customerOnboardingConfigPath = "assets/customer/json/onboarding.json"; const projectUrl = "https://coi.me"; const issueUrl = "https://github.com/open-xchange/ox-coi/issues"; const featureRequestUrl = "https://openxchange.userecho.com/communities/4-ox-coi-messenger"; +const kNotificationChannelMainId = 'com.android.oxcoi.notification.single'; +const kNotificationChannelGroupId = 'com.android.oxcoi.notification.group'; // IMAP https://tools.ietf.org/html/rfc5530 const imapErrorAuthenticationFailed = '[AUTHENTICATIONFAILED]'; diff --git a/lib/src/utils/http.dart b/lib/src/utils/http.dart index f498df90..d0ae3727 100644 --- a/lib/src/utils/http.dart +++ b/lib/src/utils/http.dart @@ -45,7 +45,7 @@ import 'dart:io'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; -bool validateHttpResponse(Response response) { +bool isHttpResponseValid(Response response) { return response.statusCode == 200 || response.statusCode == 201; } diff --git a/pubspec.lock b/pubspec.lock index 0a291709..e22f4339 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -266,7 +266,7 @@ packages: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "6.0.9" + version: "6.0.13" fixnum: dependency: transitive description: @@ -311,7 +311,14 @@ packages: name: flutter_local_notifications url: "https://pub.dartlang.org" source: hosted - version: "0.9.1+3" + version: "1.4.2" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flutter_localizations: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index b98b6f0a..47916aa6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,11 +22,11 @@ dependencies: date_format: ^1.0.8 device_info: ^0.4.2+1 equatable: ^1.0.2 - firebase_messaging: ^6.0.9 + firebase_messaging: ^6.0.13 file_picker: ^1.5.1 flutter_bloc: ^3.1.0 flutter_html: ^0.11.1 - flutter_local_notifications: ^0.9.1+3 + flutter_local_notifications: ^1.4.2 flutter_markdown: ^0.3.4 flutter_sound: git: diff --git a/test/push/push.dart b/test/push/push.dart index 15df5977..1e69de13 100644 --- a/test/push/push.dart +++ b/test/push/push.dart @@ -48,7 +48,6 @@ import 'package:pointycastle/export.dart'; import 'package:test/test.dart'; import 'package:uuid/uuid.dart'; - void main() { test('p256dh', () { var domainParameters = ECCurve_secp256r1(); @@ -57,10 +56,11 @@ void main() { generator.init(ParametersWithRandom(params, getSecureRandom())); var generateKeyPair = generator.generateKeyPair(); ECPublicKey publicKey = generateKeyPair.publicKey; - print("Point: ${publicKey.Q}"); + print("Public Point: ${publicKey.Q}"); var bytes = utf8.encode(publicKey.Q.toString()); - var base64Str = base64Url.encode(bytes); - print("Base64 $base64Str"); + var base64Str = base64UrlEncode(bytes); + ECPrivateKey privateKey = generateKeyPair.privateKey; + print("Private Point: ${privateKey.d}"); }); test('UUID', () { @@ -69,7 +69,6 @@ void main() { }); } - SecureRandom getSecureRandom() { var secureRandom = FortunaRandom(); var random = Random.secure(); @@ -77,6 +76,6 @@ SecureRandom getSecureRandom() { for (int i = 0; i < 32; i++) { seeds.add(random.nextInt(255)); } - secureRandom.seed(new KeyParameter(Uint8List.fromList(seeds))); + secureRandom.seed(KeyParameter(Uint8List.fromList(seeds))); return secureRandom; } \ No newline at end of file