diff --git a/Classes/ChatRoomTableViewController.m b/Classes/ChatRoomTableViewController.m index 3dfe02b57..9c3450c36 100644 --- a/Classes/ChatRoomTableViewController.m +++ b/Classes/ChatRoomTableViewController.m @@ -41,8 +41,6 @@ - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [TUNinePatchCache flushCache]; // Clear cache -// [self clearMessageList]; -// chatRoom = NULL; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; @@ -70,7 +68,7 @@ linphone_chat_room_get_peer_address(chatRoom) && linphone_chat_message_is_outgoing(ftd.message)) { LOGI(@"Appending transient upload message %p", ftd.message); - self->messageList = ms_list_append(self->messageList, ftd.message); + self->messageList = ms_list_append(self->messageList, linphone_chat_message_ref(ftd.message)); } } } diff --git a/Classes/ChatRoomViewController.m b/Classes/ChatRoomViewController.m index fbcbc6ac9..bccae68ce 100644 --- a/Classes/ChatRoomViewController.m +++ b/Classes/ChatRoomViewController.m @@ -549,7 +549,6 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta - (BOOL)chatRoomStartImageUpload:(UIImage*)image url:(NSURL*)url{ FileTransferDelegate * fileTransfer = [[FileTransferDelegate alloc] init]; - [[[LinphoneManager instance] fileTransferDelegates] addObject:fileTransfer]; [fileTransfer upload:image withURL:url forChatRoom:chatRoom]; [tableController addChatEntry:linphone_chat_message_ref(fileTransfer.message)]; [tableController scrollToBottom:true]; diff --git a/Classes/LinphoneUI/UIChatRoomCell.m b/Classes/LinphoneUI/UIChatRoomCell.m index 21d11c0c1..4fd1734bd 100644 --- a/Classes/LinphoneUI/UIChatRoomCell.m +++ b/Classes/LinphoneUI/UIChatRoomCell.m @@ -86,22 +86,44 @@ static UIFont *CELL_FONT = nil; - (void)dealloc { [self disconnectFromFileDelegate]; + if (self->chat) { + linphone_chat_message_unref(self->chat); + linphone_chat_message_set_user_data(self->chat, NULL); + linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), NULL); + self->chat = NULL; + } } #pragma mark - - (void)setChatMessage:(LinphoneChatMessage *)message { if (message != self->chat) { + if (self->chat) { + linphone_chat_message_unref(self->chat); + linphone_chat_message_set_user_data(self->chat, NULL); + linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), NULL); + } self->chat = message; messageImageView.image = nil; [self disconnectFromFileDelegate]; - for (FileTransferDelegate *aftd in [[LinphoneManager instance] fileTransferDelegates]) { - if (message && - linphone_chat_message_get_storage_id(aftd.message) == linphone_chat_message_get_storage_id(message)) { - LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", message, - linphone_chat_message_get_user_data(message)); - [self connectToFileDelegate:aftd]; - break; + if (self->chat) { + linphone_chat_message_ref(self->chat); + linphone_chat_message_set_user_data(self->chat, (void *)CFBridgingRetain(self)); + linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), + message_status); + + const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(message); + if (c) { + const char *name = linphone_content_get_name(c); + for (FileTransferDelegate *aftd in [[LinphoneManager instance] fileTransferDelegates]) { + if (linphone_chat_message_get_file_transfer_information(aftd.message) && + strcmp(name, linphone_content_get_name( + linphone_chat_message_get_file_transfer_information(aftd.message))) == 0) { + LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", message, aftd); + [self connectToFileDelegate:aftd]; + break; + } + } } } [self update]; @@ -353,7 +375,6 @@ static UIFont *CELL_FONT = nil; - (IBAction)onDownloadClick:(id)event { if (ftd.message == nil) { ftd = [[FileTransferDelegate alloc] init]; - [[[LinphoneManager instance] fileTransferDelegates] addObject:ftd]; [self connectToFileDelegate:ftd]; [ftd download:chat]; _cancelButton.hidden = NO; @@ -423,6 +444,11 @@ static UIFont *CELL_FONT = nil; } } } +#pragma mark - State changed handler +static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState state) { + UIChatRoomCell *thiz = (__bridge UIChatRoomCell *)linphone_chat_message_get_user_data(msg); + [thiz update]; +} #pragma mark - LinphoneFileTransfer Notifications Handling diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 310476e0e..de3e54c4f 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -19,9 +19,18 @@ } } ++ (FileTransferDelegate *)messageDelegate:(LinphoneChatMessage *)message { + for (FileTransferDelegate *ftd in [[LinphoneManager instance] fileTransferDelegates]) { + if (ftd.message == message) { + return ftd; + } + } + return nil; +} + static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, const LinphoneContent *content, const LinphoneBuffer *buffer) { - FileTransferDelegate *thiz = (__bridge FileTransferDelegate *)linphone_chat_message_get_user_data(message); + FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; size_t size = linphone_buffer_get_size(buffer); if (!thiz.data) { @@ -35,7 +44,7 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con // we're finished, save the image and update the message UIImage *image = [UIImage imageWithData:thiz.data]; - + CFBridgingRetain(thiz); [[[LinphoneManager instance] fileTransferDelegates] removeObject:thiz]; [[LinphoneManager instance] @@ -59,6 +68,7 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con forKey:@"localimage" inMessage:message]; } + linphone_chat_message_unref(thiz.message); thiz.message = NULL; [[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneFileTransferRecvUpdate @@ -88,7 +98,7 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent *content, size_t offset, size_t size) { - FileTransferDelegate *thiz = (__bridge FileTransferDelegate *)linphone_chat_message_get_user_data(message); + FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; size_t total = thiz.data.length; if (thiz.data) { size_t remaining = total - offset; @@ -97,16 +107,26 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m @"state" : @(linphone_chat_message_get_state(message)), @"progress" : @(offset * 1.f / total), }]; - LOGD(@"Transfer of %s (%d bytes): already sent %ld, remaining %ld", linphone_content_get_name(content), total, - offset, remaining); + LOGD(@"Transfer of %s (%d bytes): already sent %ld (%f%%), remaining %ld", linphone_content_get_name(content), + total, offset, offset * 100.f / total, remaining); [[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneFileTransferSendUpdate object:thiz userInfo:dict]; + + LinphoneBuffer *buffer = NULL; @try { - return linphone_buffer_new_from_data([thiz.data subdataWithRange:NSMakeRange(offset, size)].bytes, size); + buffer = linphone_buffer_new_from_data([thiz.data subdataWithRange:NSMakeRange(offset, size)].bytes, size); } @catch (NSException *exception) { LOGE(@"Exception: %@", exception); } + + // this is the last time we will be notified, so destroy ourselve + if (remaining <= size) { + linphone_chat_message_unref(thiz.message); + thiz.message = NULL; + [thiz stopAndDestroy]; + } + return buffer; } else { LOGE(@"Transfer of %s (%d bytes): %d Error - no upload data in progress!", linphone_content_get_name(content), total, offset); @@ -115,28 +135,9 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m return NULL; } -static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState state) { - FileTransferDelegate *thiz = (__bridge FileTransferDelegate *)linphone_chat_message_get_user_data(msg); - - NSString *notification = - linphone_chat_message_is_outgoing(msg) ? kLinphoneFileTransferSendUpdate : kLinphoneFileTransferRecvUpdate; - - const char *text = (linphone_chat_message_get_file_transfer_information(msg) != NULL) - ? "photo transfer" - : linphone_chat_message_get_text(msg); - LOGI(@"Delivery status for [%s] is [%s]", text, linphone_chat_message_state_to_string(state)); - - NSDictionary *dict = @{ @"state" : @(state), @"progress" : @0.f }; - if (state == LinphoneChatMessageStateFileTransferDone || state == LinphoneChatMessageStateFileTransferError) { - thiz.message = NULL; - } - [[NSNotificationCenter defaultCenter] postNotificationName:notification object:thiz userInfo:dict]; - if (linphone_chat_message_is_outgoing(msg)) { - [thiz stopAndDestroy]; - } -} - - (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom { + [[[LinphoneManager instance] fileTransferDelegates] addObject:self]; + LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_lc(chatRoom)); _data = [NSMutableData dataWithData:UIImageJPEGRepresentation(image, 1.0)]; linphone_content_set_type(content, "image"); @@ -146,38 +147,36 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st [NSDate timeIntervalSinceReferenceDate]] UTF8String]); linphone_content_set_size(content, [_data length]); - CFTypeRef myself = (__bridge CFTypeRef)self; _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); linphone_chat_message_ref(_message); - linphone_chat_message_set_user_data(_message, (void *)CFRetain(myself)); linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message), linphone_iphone_file_transfer_send); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(_message), message_status); if (url) { // internal url is saved in the appdata for display and later save [LinphoneManager setValueInMessageAppData:[url absoluteString] forKey:@"localimage" inMessage:_message]; } + LOGI(@"%p Uploading content in %p", self, _message); + linphone_chat_room_send_chat_message(chatRoom, _message); } - (BOOL)download:(LinphoneChatMessage *)message { + [[[LinphoneManager instance] fileTransferDelegates] addObject:self]; + _message = message; // we need to keep a ref on the message to continue downloading even if user quit a chatroom which destroy all chat // messages linphone_chat_message_ref(_message); const char *url = linphone_chat_message_get_external_body_url(_message); - LOGI(@"Content to download: %s", url); + LOGI(@"%p Downloading content in %p from %s", self, message, url); if (url == nil) return FALSE; - linphone_chat_message_set_user_data(_message, (void *)CFBridgingRetain(self)); - linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(_message), linphone_iphone_file_transfer_recv); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(_message), message_status); linphone_chat_message_download_file(_message); @@ -187,18 +186,17 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st - (void)stopAndDestroy { [[[LinphoneManager instance] fileTransferDelegates] removeObject:self]; if (_message != NULL) { - linphone_chat_message_set_user_data(_message, NULL); - + LOGI(@"%p Cancelling transferm from %p", self, _message); linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), NULL); linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message), NULL); linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(_message), NULL); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(_message), NULL); linphone_chat_message_cancel_file_transfer(_message); linphone_chat_message_unref(_message); } _message = nil; _data = nil; + LOGI(@"%p Destroying", self); } - (void)cancel { diff --git a/KifTests/ChatTester.m b/KifTests/ChatTester.m index f4b94654c..984c98105 100644 --- a/KifTests/ChatTester.m +++ b/KifTests/ChatTester.m @@ -35,6 +35,7 @@ if ([tester tryFindingTappableViewWithAccessibilityLabel:@"Back" error:nil]) { [self goBackFromChat]; } + ASSERT_EQ([LinphoneManager instance].fileTransferDelegates.count, 0) } #pragma mark - tools @@ -163,11 +164,11 @@ return tv; } -- (void)uploadImage { - NSString *user = @"testios"; - - [self startChatWith:user]; +- (void)uploadImageWithQuality:(NSString *)quality { + UITableView *tv = [self findTableView:@"Chat list"]; + long messagesCount = [tv numberOfRowsInSection:0]; + long delegatesCount = [[[LinphoneManager instance] fileTransferDelegates] count]; [tester tapViewWithAccessibilityLabel:@"Send picture"]; [tester tapViewWithAccessibilityLabel:@"Photo library"]; // if popup "Linphone would access your photo" pops up, click OK. @@ -177,32 +178,33 @@ #endif } - [tester choosePhotoInAlbum:@"Camera Roll" atRow:1 column:1]; + // select another photo if already uploading one + [tester choosePhotoInAlbum:@"Camera Roll" atRow:1 + delegatesCount column:1]; // wait for the quality popup to show up [tester waitForTimeInterval:1]; UIAccessibilityElement *element = [[UIApplication sharedApplication] accessibilityElementMatchingBlock:^BOOL(UIAccessibilityElement *element) { - return [element.accessibilityLabel containsString:@"Minimum ("]; + return [element.accessibilityLabel containsString:quality]; }]; [tester tapViewWithAccessibilityLabel:element.accessibilityLabel]; - UITableView *tv = [self findTableView:@"Chat list"]; - ASSERT_EQ([tv numberOfRowsInSection:0], 1); - ASSERT_EQ([[[LinphoneManager instance] fileTransferDelegates] count], 1); + ASSERT_EQ([tv numberOfRowsInSection:0], messagesCount + 1); + ASSERT_EQ([[[LinphoneManager instance] fileTransferDelegates] count], delegatesCount + 1); } - (void)testUploadImage { - NSString *user = @"testios"; + NSString *myself = @"testios"; + [self startChatWith:myself]; ASSERT_EQ([[LinphoneManager instance] fileTransferDelegates].count, 0); - [self uploadImage]; + [self uploadImageWithQuality:@"Minimum"]; ASSERT_EQ([[LinphoneManager instance] fileTransferDelegates].count, 1); [self goBackFromChat]; // if we go back to the same chatroom, the message should be still there - [self startChatWith:user]; + [self startChatWith:myself]; UITableView *tv = [self findTableView:@"Chat list"]; ASSERT_EQ([tv numberOfRowsInSection:0], 1); @@ -219,7 +221,9 @@ } - (void)testCancelUploadImage { - [self uploadImage]; + NSString *myself = @"testios"; + [self startChatWith:myself]; + [self uploadImageWithQuality:@"Minimum"]; [tester tapViewWithAccessibilityLabel:@"Cancel transfer"]; if ([[[LinphoneManager instance] fileTransferDelegates] count] != 0) { [[UIApplication sharedApplication] writeScreenshotForLine:__LINE__ inFile:@__FILE__ description:nil error:NULL]; @@ -228,8 +232,29 @@ ASSERT_EQ([[[LinphoneManager instance] fileTransferDelegates] count], 0); } +- (void)test3UploadsSimultanously { + NSString *myself = @"testios"; + [self startChatWith:myself]; + // use Maximum quality to be sure that first transfer is not terminated when the third begins + [self uploadImageWithQuality:@"Maximum"]; + [self uploadImageWithQuality:@"Maximum"]; + [self uploadImageWithQuality:@"Minimum"]; + UITableView *tv = [self findTableView:@"Chat list"]; + ASSERT_EQ([[LinphoneManager instance] fileTransferDelegates].count, 3); + // wait for ALL uploads to terminate... + for (int i = 0; i < 45; i++) { + [tester waitForTimeInterval:1.f]; + if ([tv numberOfRowsInSection:0] == 6) + break; + } + ASSERT_EQ([[LinphoneManager instance] fileTransferDelegates].count, 0); + ASSERT_EQ([tv numberOfRowsInSection:0], 6); +} + - (void)downloadImage { - [self uploadImage]; + NSString *myself = @"testios"; + [self startChatWith:myself]; + [self uploadImageWithQuality:@"Minimum"]; // wait for the upload to terminate... for (int i = 0; i < 15; i++) { [tester waitForTimeInterval:1.f]; @@ -242,6 +267,26 @@ ASSERT_EQ([[[LinphoneManager instance] fileTransferDelegates] count], 1); } +- (void)test3DownloadsSimultanously { + [self startChatWith:[self me]]; + [self uploadImageWithQuality:@"Maximum"]; + [self uploadImageWithQuality:@"Average"]; + [self uploadImageWithQuality:@"Minimum"]; + UITableView *tv = [self findTableView:@"Chat list"]; + // wait for ALL uploads to terminate... + for (int i = 0; i < 45; i++) { + [tester waitForTimeInterval:1.f]; + if ([tv numberOfRowsInSection:0] == 6) + break; + } + ASSERT_EQ([[LinphoneManager instance] fileTransferDelegates].count, 0); + for (int i = 0; i < 3; i++) { + [tester waitForViewWithAccessibilityLabel:@"Download"]; + [tester tapViewWithAccessibilityLabel:@"Download"]; + [tester waitForTimeInterval:.5f]; // just wait a few secs to start download + } +} + - (void)testDownloadImage { [self downloadImage]; [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Cancel transfer"]; diff --git a/Tools/imgur_upload.sh b/Tools/imgur_upload.sh index a6fefffaf..d22d53c01 100755 --- a/Tools/imgur_upload.sh +++ b/Tools/imgur_upload.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash # Install underscore-cli for hacking if ! which underscore &> /dev/null; then diff --git a/main.m b/main.m index 37ed74ae1..e73aeb18d 100644 --- a/main.m +++ b/main.m @@ -36,7 +36,6 @@ int main(int argc, char *argv[]) { NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); #endif @autoreleasepool { - int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([LinphoneAppDelegate class])); - return retVal; - } + return UIApplicationMain(argc, argv, nil, NSStringFromClass([LinphoneAppDelegate class])); + } } diff --git a/submodules/belle-sip b/submodules/belle-sip index 2f795533d..cfd87c55a 160000 --- a/submodules/belle-sip +++ b/submodules/belle-sip @@ -1 +1 @@ -Subproject commit 2f795533d68784ae58abd6a43b24281d33c5730d +Subproject commit cfd87c55acee96699ce8306a3734b477fee3a00a