123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- /**
- * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- #import "NCRoomsManager.h"
- #import <Realm/Realm.h>
- #import "AppDelegate.h"
- #import "CallKitManager.h"
- #import "NCChatBlock.h"
- #import "NCChatController.h"
- #import "NCChatMessage.h"
- #import "NCDatabaseManager.h"
- #import "NCExternalSignalingController.h"
- #import "NCSettingsController.h"
- #import "NCUserInterfaceController.h"
- #import "NotificationCenterNotifications.h"
- #import "NextcloudTalk-Swift.h"
- NSString * const NCRoomsManagerDidJoinRoomNotification = @"NCRoomsManagerDidJoinRoomNotification";
- NSString * const NCRoomsManagerDidLeaveRoomNotification = @"NCRoomsManagerDidLeaveRoomNotification";
- NSString * const NCRoomsManagerDidUpdateRoomsNotification = @"NCRoomsManagerDidUpdateRoomsNotification";
- NSString * const NCRoomsManagerDidUpdateRoomNotification = @"NCRoomsManagerDidUpdateRoomNotification";
- NSString * const NCRoomsManagerDidStartCallNotification = @"NCRoomsManagerDidStartCallNotification";
- NSString * const NCRoomsManagerDidReceiveChatMessagesNotification = @"ChatMessagesReceivedNotification";
- static NSInteger kNotJoiningAnymoreStatusCode = 999;
- @interface NCRoomsManager () <CallViewControllerDelegate>
- @end
- @implementation NCRoomController
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- [[AllocationTracker shared] addAllocation:@"NCRoomController"];
- }
- return self;
- }
- - (void)dealloc
- {
- [[AllocationTracker shared] removeAllocation:@"NCRoomController"];
- }
- @end
- @implementation NCRoomsManager
- + (NCRoomsManager *)sharedInstance
- {
- static dispatch_once_t once;
- static NCRoomsManager *sharedInstance;
- dispatch_once(&once, ^{
- sharedInstance = [[self alloc] init];
- });
- return sharedInstance;
- }
- - (id)init
- {
- self = [super init];
- if (self) {
- _activeRooms = [[NSMutableDictionary alloc] init];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinChatWithLocalNotification:) name:NCLocalNotificationJoinChatNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinChat:) name:NCPushNotificationJoinChatNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinAudioCallAccepted:) name:NCPushNotificationJoinAudioCallAcceptedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinVideoCallAccepted:) name:NCPushNotificationJoinVideoCallAcceptedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectedUserForChat:) name:NCSelectedUserForChatNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomCreated:) name:NCRoomCreatedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptCallForRoom:) name:CallKitManagerDidAnswerCallNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startCallForRoom:) name:CallKitManagerDidStartCallNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkForCallUpgrades:) name:CallKitManagerDidEndCallNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinOrCreateChat:) name:NSNotification.NCChatViewControllerReplyPrivatelyNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinChatOfForwardedMessage:) name:NSNotification.NCChatViewControllerForwardNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinOrCreateChat:) name:NSNotification.NCChatViewControllerTalkToUserNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinOrCreateChatWithURL:) name:NCURLWantsToOpenConversationNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(joinChatHighlightingMessage:) name:NCPresentChatHighlightingMessageNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionStateHasChanged:) name:NCConnectionStateHasChangedNotification object:nil];
- }
-
- return self;
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- #pragma mark - Room
- - (void)updateRoomsUpdatingUserStatus:(BOOL)updateStatus onlyLastModified:(BOOL)onlyLastModified
- {
- [self updateRoomsUpdatingUserStatus:updateStatus onlyLastModified:onlyLastModified withCompletionBlock:nil];
- }
- - (void)updateRoomsAndChatsUpdatingUserStatus:(BOOL)updateStatus onlyLastModified:(BOOL)onlyLastModified withCompletionBlock:(UpdateRoomsAndChatsCompletionBlock)block
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- if (onlyLastModified && [activeAccount.lastReceivedModifiedSince integerValue] == 0) {
- if (block) {
- block(nil);
- }
- return;
- }
- [self updateRoomsUpdatingUserStatus:updateStatus onlyLastModified:onlyLastModified withCompletionBlock:^(NSArray *roomsWithNewMessages, TalkAccount *account, NSError *error) {
- if (error) {
- if (block) {
- block(error);
- }
-
- return;
- }
-
- NSLog(@"Finished rooms update with %lu rooms with new messages", [roomsWithNewMessages count]);
- dispatch_group_t chatUpdateGroup = dispatch_group_create();
-
- // When in low power mode, we only update the conversation list and don't load new messages for each room
- if (![NSProcessInfo processInfo].isLowPowerModeEnabled && [[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityChatKeepNotifications forAccountId:account.accountId]) {
- for (NCRoom *room in roomsWithNewMessages) {
- dispatch_group_enter(chatUpdateGroup);
- NSLog(@"Updating room %@", room.internalId);
- NCChatController *chatController;
- if (self.chatViewController && self.chatViewController.chatController && [self.chatViewController.room.internalId isEqualToString:room.internalId]) {
- // If there's already a chatController for this room, don't create a new one
- chatController = self.chatViewController.chatController;
- } else {
- chatController = [[NCChatController alloc] initForRoom:room];
- }
- [chatController updateHistoryInBackgroundWithCompletionBlock:^(NSError *error) {
- NSLog(@"Finished updating room %@", room.internalId);
- dispatch_group_leave(chatUpdateGroup);
- }];
- }
- }
-
- dispatch_group_notify(chatUpdateGroup, dispatch_get_main_queue(), ^{
- // Notify backgroundFetch that we're finished
- if (block) {
- block(nil);
- }
- });
- }];
- }
- - (void)updateRoomsUpdatingUserStatus:(BOOL)updateStatus onlyLastModified:(BOOL)onlyLastModified withCompletionBlock:(UpdateRoomsCompletionBlock)block
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- NSInteger modifiedSince = onlyLastModified ? [activeAccount.lastReceivedModifiedSince integerValue] : 0;
- [[NCAPIController sharedInstance] getRoomsForAccount:activeAccount updateStatus:updateStatus modifiedSince:modifiedSince completionBlock:^(NSArray * _Nullable rooms, NSError * _Nullable error) {
- NSMutableDictionary *userInfo = [NSMutableDictionary new];
- NSMutableArray *roomsWithNewMessages = [NSMutableArray new];
- if (!error) {
- BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"NCUpdateRoomsTransaction" expirationHandler:^(BGTaskHelper *task) {
- NSString *logMessage = [NSString stringWithFormat:@"ExpirationHandler called NCUpdateRoomsTransaction, number of rooms %ld", rooms.count];
- [NCUtils log:logMessage];
- }];
- RLMRealm *realm = [RLMRealm defaultRealm];
- NSInteger updateTimestamp = [[NSDate date] timeIntervalSince1970];
- [realm transactionWithBlock:^{
- // Add or update rooms
- for (NSDictionary *roomDict in rooms) {
- BOOL roomContainsNewMessages = [self updateRoomWithDict:roomDict withAccount:activeAccount withTimestamp:updateTimestamp withRealm:realm];
- if (roomContainsNewMessages) {
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:activeAccount.accountId];
- [roomsWithNewMessages addObject:room];
- }
- }
- }];
- // Only delete rooms if it was a complete rooms update (not using modifiedSince)
- if (!onlyLastModified) {
- [realm transactionWithBlock:^{
- // Delete old rooms
- NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@ AND lastUpdate != %ld", activeAccount.accountId, (long)updateTimestamp];
- RLMResults *managedRoomsToBeDeleted = [NCRoom objectsWithPredicate:query];
- // Delete messages and chat blocks from old rooms
- for (NCRoom *managedRoom in managedRoomsToBeDeleted) {
- NSPredicate *query2 = [NSPredicate predicateWithFormat:@"accountId = %@ AND token = %@", activeAccount.accountId, managedRoom.token];
- [realm deleteObjects:[NCChatMessage objectsWithPredicate:query2]];
- [realm deleteObjects:[NCChatBlock objectsWithPredicate:query2]];
- if ([managedRoom isFederated]) {
- NSPredicate *federatedCapabilities = [NSPredicate predicateWithFormat:@"accountId = %@ AND remoteServer = %@ AND roomToken = %@", activeAccount.accountId, managedRoom.remoteServer, managedRoom.token];
- [realm deleteObjects:[FederatedCapabilities objectsWithPredicate:federatedCapabilities]];
- }
- }
- [realm deleteObjects:managedRoomsToBeDeleted];
- }];
- }
- [bgTask stopBackgroundTask];
- } else {
- [userInfo setObject:error forKey:@"error"];
- [NCUtils log:[NSString stringWithFormat:@"Could not update rooms. Error: %@", error.description]];
- }
-
- [[NSNotificationCenter defaultCenter] postNotificationName:NCRoomsManagerDidUpdateRoomsNotification
- object:self
- userInfo:userInfo];
-
- if (block) {
- block(roomsWithNewMessages, activeAccount, error);
- }
- }];
- }
- - (void)updateRoom:(NSString *)token withCompletionBlock:(GetRoomCompletionBlock)block
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCAPIController sharedInstance] getRoomForAccount:activeAccount withToken:token completionBlock:^(NSDictionary *roomDict, NSError *error) {
- NSMutableDictionary *userInfo = [NSMutableDictionary new];
- if (!error) {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm transactionWithBlock:^{
- [self updateRoomWithDict:roomDict withAccount:activeAccount withTimestamp:[[NSDate date] timeIntervalSince1970] withRealm:realm];
- NSLog(@"Room updated");
- }];
- NCRoom *updatedRoom = [[NCDatabaseManager sharedInstance] roomWithToken:token forAccountId:activeAccount.accountId];
- if (updatedRoom) {
- // It seems to be somehow possible to have the updatedRoom be nil as seen in AppStore crashes
- [userInfo setObject:updatedRoom forKey:@"room"];
- }
- } else {
- [userInfo setObject:error forKey:@"error"];
- NSLog(@"Could not update rooms. Error: %@", error.description);
- }
- [[NSNotificationCenter defaultCenter] postNotificationName:NCRoomsManagerDidUpdateRoomNotification
- object:self
- userInfo:userInfo];
- if (block) {
- block(roomDict, error);
- }
- }];
- }
- - (BOOL)updateRoomWithDict:(NSDictionary *)roomDict withAccount:(TalkAccount *)activeAccount withTimestamp:(NSInteger)timestamp withRealm:(RLMRealm *)realm
- {
- BOOL roomContainsNewMessages = NO;
-
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:activeAccount.accountId];
- room.lastUpdate = timestamp;
- NCChatMessage *lastMessage = nil;
- NSDictionary *messageDict = [roomDict objectForKey:@"lastMessage"];
- if (!room.isFederated) {
- // TODO: Move handling to NCRoom roomWithDictionary?
- lastMessage = [NCChatMessage messageWithDictionary:messageDict andAccountId:activeAccount.accountId];
- room.lastMessageId = lastMessage.internalId;
- }
-
- NCRoom *managedRoom = [NCRoom objectsWhere:@"internalId = %@", room.internalId].firstObject;
- if (managedRoom) {
- if (room.lastActivity > managedRoom.lastActivity) {
- roomContainsNewMessages = YES;
- }
-
- [NCRoom updateRoom:managedRoom withRoom:room];
- } else if (room) {
- [realm addObject:room];
- }
- if (lastMessage) {
- NCChatMessage *managedLastMessage = [NCChatMessage objectsWhere:@"internalId = %@", lastMessage.internalId].firstObject;
- if (managedLastMessage) {
- [NCChatMessage updateChatMessage:managedLastMessage withChatMessage:lastMessage isRoomLastMessage:YES];
- } else {
- NCChatController *chatController = [[NCChatController alloc] initForRoom:room];
- [chatController storeMessages:@[messageDict] withRealm:realm];
- }
- }
-
- return roomContainsNewMessages;
- }
- - (void)updatePendingMessage:(NSString *)message forRoom:(NCRoom *)room
- {
- BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"updatePendingMessage" expirationHandler:nil];
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm transactionWithBlock:^{
- NCRoom *managedRoom = [NCRoom objectsWhere:@"internalId = %@", room.internalId].firstObject;
- if (managedRoom) {
- managedRoom.pendingMessage = message;
- }
- }];
- [bgTask stopBackgroundTask];
- }
- - (void)updateLastReadMessage:(NSInteger)lastReadMessage forRoom:(NCRoom *)room
- {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm transactionWithBlock:^{
- NCRoom *managedRoom = [NCRoom objectsWhere:@"internalId = %@", room.internalId].firstObject;
- if (managedRoom) {
- managedRoom.lastReadMessage = lastReadMessage;
- }
- }];
- }
- - (void)updateLastMessage:(NCChatMessage *)message withNoUnreadMessages:(BOOL)noUnreadMessages forRoom:(NCRoom *)room
- {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm transactionWithBlock:^{
- NCRoom *managedRoom = [NCRoom objectsWhere:@"internalId = %@", room.internalId].firstObject;
- if (managedRoom) {
- managedRoom.lastMessageId = message.internalId;
- managedRoom.lastActivity = message.timestamp;
-
- if (noUnreadMessages) {
- managedRoom.unreadMention = NO;
- managedRoom.unreadMentionDirect = NO;
- managedRoom.unreadMessages = 0;
- }
- }
- }];
- }
- - (void)updateLastCommonReadMessage:(NSInteger)messageId forRoom:(NCRoom *)room
- {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm transactionWithBlock:^{
- NCRoom *managedRoom = [NCRoom objectsWhere:@"internalId = %@", room.internalId].firstObject;
- if (managedRoom && messageId > managedRoom.lastCommonReadMessage) {
- managedRoom.lastCommonReadMessage = messageId;
- }
- }];
- }
- #pragma mark - Chat
- - (void)startChatInRoom:(NCRoom *)room
- {
- if (_callViewController) {
- NSLog(@"Not starting chat due to in a call.");
- return;
- }
-
- NCRoomController *roomController = [_activeRooms objectForKey:room.token];
- if (!roomController) {
- // Workaround until external signaling supports multi-room
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- NCExternalSignalingController *extSignalingController = [[NCSettingsController sharedInstance] externalSignalingControllerForAccountId:activeAccount.accountId];
- if (extSignalingController) {
- NSString *currentRoom = extSignalingController.currentRoom;
- if (![currentRoom isEqualToString:room.token]) {
- // Since we are going to join another conversation, we don't need to leaveRoom() in extSignalingController.
- // That's why we set currentRoom = nil, so when leaveRoom() is called in extSignalingController the currentRoom
- // is no longer the room we want to leave (so no message is sent to the external signaling server).
- extSignalingController.currentRoom = nil;
- }
- }
- }
-
- if (!_chatViewController || ![_chatViewController.room.token isEqualToString:room.token]) {
- // Leave the previous chat
- //[[NCRoomsManager sharedInstance].chatViewController leaveChat];
- NSLog(@"Creating new chat view controller.");
- _chatViewController = [[ChatViewController alloc] initFor:room];
- if (_highlightMessageDict && [[_highlightMessageDict objectForKey:@"token"] isEqualToString:room.token]) {
- _chatViewController.highlightMessageId = [[_highlightMessageDict objectForKey:@"messageId"] integerValue];
- _highlightMessageDict = nil;
- }
- [[NCUserInterfaceController sharedInstance] presentChatViewController:_chatViewController];
- } else {
- NSLog(@"Not creating new chat room: chatViewController for room %@ does already exist.", room.token);
- // Still make sure the current room is highlighted
- [[NCUserInterfaceController sharedInstance].roomsTableViewController setSelectedRoomToken:_chatViewController.room.token];
- }
- }
- - (void)startChatWithRoomToken:(NSString *)token
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- NCRoom *room = [[NCDatabaseManager sharedInstance] roomWithToken:token forAccountId:activeAccount.accountId];
- if (room) {
- [self startChatInRoom:room];
- } else {
- //TODO: Show spinner?
- [[NCAPIController sharedInstance] getRoomForAccount:activeAccount withToken:token completionBlock:^(NSDictionary *roomDict, NSError *error) {
- if (!error) {
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:activeAccount.accountId];
- [self startChatInRoom:room];
- }
- }];
- }
- }
- - (void)leaveChatInRoom:(NSString *)token;
- {
- NCRoomController *roomController = [_activeRooms objectForKey:token];
- if (roomController) {
- roomController.inChat = NO;
- }
-
- [self leaveRoom:token];
- }
- #pragma mark - Call
- - (void)startCall:(BOOL)video inRoom:(NCRoom *)room withVideoEnabled:(BOOL)enabled asInitiator:(BOOL)initiator silently:(BOOL)silently recordingConsent:(BOOL)recordingConsent andVoiceChatMode:(BOOL)voiceChatMode
- {
- if (!_callViewController) {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- _callViewController = [[CallViewController alloc] initCallInRoom:room asUser:activeAccount.userDisplayName audioOnly:!video];
- _callViewController.videoDisabledAtStart = !enabled;
- _callViewController.voiceChatModeAtStart = voiceChatMode;
- _callViewController.initiator = initiator;
- _callViewController.silentCall = silently;
- _callViewController.recordingConsent = recordingConsent;
- [_callViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
- _callViewController.delegate = self;
- NSString *chatViewControllerRoomToken = _chatViewController.room.token;
- NSString *joiningRoomToken = room.token;
- // Workaround until external signaling supports multi-room
- NCExternalSignalingController *extSignalingController = [[NCSettingsController sharedInstance] externalSignalingControllerForAccountId:activeAccount.accountId];
- if (extSignalingController) {
- NSString *extSignalingRoomToken = extSignalingController.currentRoom;
- if (![extSignalingRoomToken isEqualToString:joiningRoomToken]) {
- // Since we are going to join another conversation, we don't need to leaveRoom() in extSignalingController.
- // That's why we set currentRoom = nil, so when leaveRoom() is called in extSignalingController the currentRoom
- // is no longer the room we want to leave (so no message is sent to the external signaling server).
- extSignalingController.currentRoom = nil;
- }
- }
- // Make sure the external signaling contoller is connected.
- // Could be that the call has been received while the app was inactive or in the background,
- // so the external signaling controller might be disconnected at this point.
- if ([extSignalingController disconnected]) {
- [extSignalingController forceConnect];
- }
- if (_chatViewController) {
- if ([chatViewControllerRoomToken isEqualToString:joiningRoomToken]) {
- // We're in the chat of the room we want to start a call, so stop chat for now
- [_chatViewController stopChat];
- } else {
- // We're in a different chat, so make sure we leave the chat and go back to the conversation list
- [_chatViewController leaveChat];
- [[NCUserInterfaceController sharedInstance] presentConversationsList];
- }
- }
- [[NCUserInterfaceController sharedInstance] presentCallViewController:_callViewController withCompletionBlock:^{
- [self joinRoom:room.token forCall:YES];
- }];
- } else {
- NSLog(@"Not starting call due to in another call.");
- }
- }
- - (void)joinCallWithCallToken:(NSString *)token withVideo:(BOOL)video asInitiator:(BOOL)initiator recordingConsent:(BOOL)recordingConsent
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCAPIController sharedInstance] getRoomForAccount:activeAccount withToken:token completionBlock:^(NSDictionary *roomDict, NSError *error) {
- if (!error) {
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:activeAccount.accountId];
- [[CallKitManager sharedInstance] startCall:room.token withVideoEnabled:video andDisplayName:room.displayName asInitiator:initiator silently:YES recordingConsent:recordingConsent withAccountId:activeAccount.accountId];
- }
- }];
- }
- - (void)startCallWithCallToken:(NSString *)token withVideo:(BOOL)video enabledAtStart:(BOOL)enabled asInitiator:(BOOL)initiator silently:(BOOL)silently recordingConsent:(BOOL)recordingConsent andVoiceChatMode:(BOOL)voiceChatMode
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCAPIController sharedInstance] getRoomForAccount:activeAccount withToken:token completionBlock:^(NSDictionary *roomDict, NSError *error) {
- if (!error) {
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:activeAccount.accountId];
- [self startCall:video inRoom:room withVideoEnabled:enabled asInitiator:initiator silently:silently recordingConsent:recordingConsent andVoiceChatMode:voiceChatMode];
- }
- }];
- }
- - (void)checkForPendingToStartCalls
- {
- if (_pendingToStartCallToken) {
- // Pending calls can only happen when answering a new call. That's why we start with video disabled at start and in voice chat mode.
- // We also can start call silently because we are joining an already started call so no need to notify.
- [self startCallWithCallToken:_pendingToStartCallToken withVideo:_pendingToStartCallHasVideo enabledAtStart:NO asInitiator:NO silently:YES recordingConsent:NO andVoiceChatMode:YES];
- _pendingToStartCallToken = nil;
- }
- }
- - (BOOL)areThereActiveCalls
- {
- for (NCRoomController *roomController in [_activeRooms allValues]) {
- if (roomController.inCall) {
- return YES;
- }
- }
- return NO;
- }
- - (void)upgradeCallToVideoCall:(NCRoom *)room
- {
- NCRoomController *roomController = [_activeRooms objectForKey:room.token];
- if (roomController) {
- roomController.inCall = NO;
- }
- _upgradeCallToken = room.token;
- [[CallKitManager sharedInstance] endCall:room.token withStatusCode:0];
- }
- - (void)leaveCallInRoom:(NSString *)token
- {
- NCRoomController *roomController = [_activeRooms objectForKey:token];
- if (roomController) {
- roomController.inCall = NO;
- }
- [self leaveRoom:token];
- }
- - (void)callDidEndInRoomWithToken:(NSString *)token
- {
- [self leaveCallInRoom:token];
- [[CallKitManager sharedInstance] endCall:token withStatusCode:0];
-
- if ([_chatViewController.room.token isEqualToString:token]) {
- [_chatViewController resumeChat];
- }
- }
- #pragma mark - Switch to
- - (void)prepareSwitchToAnotherRoomFromRoom:(NSString *)token withCompletionBlock:(ExitRoomCompletionBlock)block
- {
- if ([_chatViewController.room.token isEqualToString:token]) {
- [_chatViewController leaveChat];
- [[NCUserInterfaceController sharedInstance] popToConversationsList];
- }
-
- // Remove room controller and exit room
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- NCRoomController *roomController = [_activeRooms objectForKey:token];
- if (roomController) {
- [_activeRooms removeObjectForKey:token];
- [[NCAPIController sharedInstance] exitRoom:token forAccount:activeAccount withCompletionBlock:block];
- } else {
- NSLog(@"Couldn't find a room controller from the room we are switching from");
- block(nil);
- }
- }
- #pragma mark - CallViewControllerDelegate
- - (void)callViewControllerWantsToBeDismissed:(CallViewController *)viewController
- {
- if (_callViewController == viewController && ![viewController isBeingDismissed]) {
- [viewController dismissViewControllerAnimated:YES completion:nil];
- }
- }
- - (void)callViewControllerWantsVideoCallUpgrade:(CallViewController *)viewController
- {
- NCRoom *room = _callViewController.room;
- if (_callViewController == viewController) {
- _callViewController = nil;
- [self upgradeCallToVideoCall:room];
- }
- }
- - (void)callViewController:(CallViewController *)viewController wantsToSwitchCallFromCall:(NSString *)from toRoom:(NSString *)to
- {
- if (_callViewController == viewController) {
- [[CallKitManager sharedInstance] switchCallFrom:from toCall:to];
- }
- }
- - (void)callViewControllerDidFinish:(CallViewController *)viewController
- {
- if (_callViewController == viewController) {
- NSString *token = [_callViewController.room.token copy];
- _callViewController = nil;
- [self callDidEndInRoomWithToken:token];
- // Keep connection alive temporarily when a call was finished while the app in the background
- if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) {
- AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
- [appDelegate keepExternalSignalingConnectionAliveTemporarily];
- }
- }
- }
- #pragma mark - Notifications
- - (void)checkForCallUpgrades:(NSNotification *)notification
- {
- if (_upgradeCallToken) {
- NSString *token = [_upgradeCallToken copy];
- _upgradeCallToken = nil;
- // Add some delay so CallKit doesn't fail requesting new call
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
- [self joinCallWithCallToken:token withVideo:YES asInitiator:NO recordingConsent:YES];
- });
- }
- }
- - (void)checkForAccountChange:(NSString *)accountId
- {
- // Change account if notification is from another account
- if (accountId && ![[[NCDatabaseManager sharedInstance] activeAccount].accountId isEqualToString:accountId]) {
- // Leave chat before changing accounts
- if ([[NCRoomsManager sharedInstance] chatViewController]) {
- [[[NCRoomsManager sharedInstance] chatViewController] leaveChat];
- }
- // Set notification account active
- [[NCSettingsController sharedInstance] setActiveAccountWithAccountId:accountId];
- }
- }
- - (void)acceptCallForRoom:(NSNotification *)notification
- {
- NSString *roomToken = [notification.userInfo objectForKey:@"roomToken"];
- BOOL waitForCallEnd = [[notification.userInfo objectForKey:@"waitForCallEnd"] boolValue];
- BOOL hasVideo = [[notification.userInfo objectForKey:@"hasVideo"] boolValue];
- BOOL activeCalls = [self areThereActiveCalls];
- if (!waitForCallEnd || (!activeCalls && !_leaveRoomTask)) {
- // Calls that have been answered start with video disabled by default, in voice chat mode and silently (without notification).
- [self startCallWithCallToken:roomToken withVideo:hasVideo enabledAtStart:NO asInitiator:NO silently:YES recordingConsent:NO andVoiceChatMode:YES];
- } else {
- _pendingToStartCallToken = roomToken;
- _pendingToStartCallHasVideo = hasVideo;
- }
- }
- - (void)startCallForRoom:(NSNotification *)notification
- {
- NSString *roomToken = [notification.userInfo objectForKey:@"roomToken"];
- BOOL isVideoEnabled = [[notification.userInfo objectForKey:@"isVideoEnabled"] boolValue];
- BOOL initiator = [[notification.userInfo objectForKey:@"initiator"] boolValue];
- BOOL silentCall = [[notification.userInfo objectForKey:@"silentCall"] boolValue];
- BOOL recordingConsent = [[notification.userInfo objectForKey:@"recordingConsent"] boolValue];
- [self startCallWithCallToken:roomToken withVideo:isVideoEnabled enabledAtStart:YES asInitiator:initiator silently:silentCall recordingConsent:recordingConsent andVoiceChatMode:NO];
- }
- - (void)joinAudioCallAccepted:(NSNotification *)notification
- {
- NCPushNotification *pushNotification = [notification.userInfo objectForKey:@"pushNotification"];
- [self checkForAccountChange:pushNotification.accountId];
- [self joinCallWithCallToken:pushNotification.roomToken withVideo:NO asInitiator:NO recordingConsent:NO];
- }
- - (void)joinVideoCallAccepted:(NSNotification *)notification
- {
- NCPushNotification *pushNotification = [notification.userInfo objectForKey:@"pushNotification"];
- [self checkForAccountChange:pushNotification.accountId];
- [self joinCallWithCallToken:pushNotification.roomToken withVideo:YES asInitiator:NO recordingConsent:NO];
- }
- - (void)joinChat:(NSNotification *)notification
- {
- NCPushNotification *pushNotification = [notification.userInfo objectForKey:@"pushNotification"];
- [self checkForAccountChange:pushNotification.accountId];
- [self startChatWithRoomToken:pushNotification.roomToken];
- }
- - (void)joinOrCreateChatWithUser:(NSString *)userId usingAccountId:(NSString *)accountId
- {
- NSArray *accountRooms = [[NCRoomsManager sharedInstance] roomsForAccountId:accountId withRealm:nil];
- for (NCRoom *room in accountRooms) {
- NSArray *participantsInRoom = [room.participants valueForKey:@"self"];
-
- if (room.type == kNCRoomTypeOneToOne && [participantsInRoom containsObject:userId]) {
- // Room already exists -> join the room
- [self startChatWithRoomToken:room.token];
-
- return;
- }
- }
-
- // Did not find a one-to-one room for this user -> create a new one
- [[NCAPIController sharedInstance] createRoomForAccount:[[NCDatabaseManager sharedInstance] activeAccount] withInvite:userId
- ofType:kNCRoomTypeOneToOne
- andName:nil
- completionBlock:^(NCRoom *room, NSError *error) {
- if (!error) {
- [self startChatWithRoomToken:room.token];
- NSLog(@"Room %@ with %@ created", room.token, userId);
- } else {
- NSLog(@"Failed creating a room with %@", userId);
- }
- }];
- }
- - (void)joinOrCreateChat:(NSNotification *)notification
- {
- NSString *actorId = [notification.userInfo objectForKey:@"actorId"];
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [self joinOrCreateChatWithUser:actorId usingAccountId:activeAccount.accountId];
- }
- - (void)joinOrCreateChatWithURL:(NSNotification *)notification
- {
- NSString *accountId = [notification.userInfo objectForKey:@"accountId"];
- NSString *withUser = [notification.userInfo objectForKey:@"withUser"];
- NSString *roomToken = [notification.userInfo objectForKey:@"withRoomToken"];
- [self checkForAccountChange:accountId];
- if (withUser) {
- [self joinOrCreateChatWithUser:withUser usingAccountId:accountId];
- } else if (roomToken) {
- [self startChatWithRoomToken:roomToken];
- } // else: no chat specified, only change account
- }
- - (void)joinChatOfForwardedMessage:(NSNotification *)notification
- {
- NSString *accountId = [notification.userInfo objectForKey:@"accountId"];
- NSString *token = [notification.userInfo objectForKey:@"token"];
- [self checkForAccountChange:accountId];
- [self startChatWithRoomToken:token];
- }
- - (void)joinChatWithLocalNotification:(NSNotification *)notification
- {
- NSString *roomToken = [notification.userInfo objectForKey:@"roomToken"];
- if (roomToken) {
- NSString *accountId = [notification.userInfo objectForKey:@"accountId"];
- [self checkForAccountChange:accountId];
- [self startChatWithRoomToken:roomToken];
-
- // In case this notification occurred because of a failed chat-sending event, make sure the text is not lost
- // Note: This will override any stored pending message
- NSString *responseUserText = [notification.userInfo objectForKey:@"responseUserText"];
- if (_chatViewController && responseUserText) {
- [_chatViewController setChatMessage:responseUserText];
- }
- }
- }
- - (void)joinChatHighlightingMessage:(NSNotification *)notification
- {
- _highlightMessageDict = notification.userInfo;
- NSString *token = [notification.userInfo objectForKey:@"token"];
- [self startChatWithRoomToken:token];
- }
- - (void)selectedUserForChat:(NSNotification *)notification
- {
- NSString *roomToken = [notification.userInfo objectForKey:@"token"];
- [self startChatWithRoomToken:roomToken];
- }
- - (void)roomCreated:(NSNotification *)notification
- {
- NSString *roomToken = [notification.userInfo objectForKey:@"token"];
- [self startChatWithRoomToken:roomToken];
- }
- - (void)connectionStateHasChanged:(NSNotification *)notification
- {
- ConnectionState connectionState = [[notification.userInfo objectForKey:@"connectionState"] intValue];
- // Try to send offline message when the connection state changes to connected again
- if (connectionState == kConnectionStateConnected) {
- [self resendOfflineMessagesWithCompletionBlock:nil];
- }
- }
- @end
|