From 6ef77ebdaa6dc0e6cf67a5733568d60afe6bdbba Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 3 May 2021 08:44:34 +0200 Subject: [PATCH] enable multipart send and recv --- Classes/ChatConversationTableView.h | 15 +- Classes/ChatConversationTableView.m | 12 +- Classes/ChatConversationView.h | 10 +- Classes/ChatConversationView.m | 201 ++++++++++++++++----- Classes/LinphoneUI/UIChatBubblePhotoCell.h | 5 +- Classes/LinphoneUI/UIChatBubblePhotoCell.m | 140 +++++++++++--- Classes/LinphoneUI/UIChatBubbleTextCell.h | 15 +- Classes/LinphoneUI/UIChatBubbleTextCell.m | 176 +++++++++++++++--- Classes/LinphoneUI/UIChatContentView.h | 34 ++++ Classes/LinphoneUI/UIChatContentView.m | 75 ++++++++ Classes/LinphoneUI/UIImageViewDeletable.h | 4 +- Classes/LinphoneUI/UIImageViewDeletable.m | 2 +- Classes/Utils/FileTransferDelegate.h | 2 + Classes/Utils/FileTransferDelegate.m | 42 ++++- linphone.xcodeproj/project.pbxproj | 6 + 15 files changed, 614 insertions(+), 125 deletions(-) create mode 100644 Classes/LinphoneUI/UIChatContentView.h create mode 100644 Classes/LinphoneUI/UIChatContentView.m diff --git a/Classes/ChatConversationTableView.h b/Classes/ChatConversationTableView.h index 017889f00..b0e701f60 100644 --- a/Classes/ChatConversationTableView.h +++ b/Classes/ChatConversationTableView.h @@ -25,10 +25,22 @@ #import "UICheckBoxTableView.h" +@interface FileContext : NSObject +@property NSMutableArray *typesArray; +@property NSMutableArray *datasArray; +@property NSMutableArray *previewsArray; +@property NSMutableArray *namesArray; +@property NSMutableArray *uuidsArray; + +- (void)clear; +- (NSUInteger)count; +- (void)addObject:(NSData *)data name:(NSString *)name type:(NSString *)type; +@end + @protocol ChatConversationDelegate +- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message; - (BOOL)resendFile:(NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message; -- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message; - (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name; - (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url; - (void)tableViewIsScrolling; @@ -45,6 +57,7 @@ @property(nonatomic) NSInteger currentIndex; @property(nonatomic, strong) id chatRoomDelegate; @property NSMutableDictionary *imagesInChatroom; +@property(nonatomic) BOOL vfsEnabled; - (void)addEventEntry:(LinphoneEventLog *)event; - (void)scrollToBottom:(BOOL)animated; diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index 2cb6664fb..584c2708b 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -183,6 +183,10 @@ #pragma mark - Property Functions - (void)setChatRoom:(LinphoneChatRoom *)room { + if (room) { + _vfsEnabled = [[LinphoneManager instance] lpConfigBoolForKey:@"vfs_enabled_preference"] && (linphone_chat_room_get_capabilities(room) & LinphoneChatRoomCapabilitiesEncrypted); + } + _chatRoom = room; [self reloadData]; } @@ -246,6 +250,12 @@ static const int BASIC_EVENT_LIST=15; return eventList.count; } +- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath { + if ([[cell reuseIdentifier] isEqualToString:@"UIChatBubblePhotoCell"]) { + [(UIChatBubbleTextCell *)cell clearEncryptedFiles]; + } +} + - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *kCellId = nil; LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue]; @@ -260,7 +270,7 @@ static const int BASIC_EVENT_LIST=15; UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; - [cell setEvent:event]; + [cell setEvent:event vfsEnabled:_vfsEnabled]; if (chat) { cell.isFirst = [self isFirstIndexInTableView:indexPath chat:chat]; cell.isLast = [self isLastIndexInTableView:indexPath chat:chat]; diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h index 3e4532df0..777f9f6bd 100644 --- a/Classes/ChatConversationView.h +++ b/Classes/ChatConversationView.h @@ -35,6 +35,7 @@ #include "linphone/linphonecore.h" + //Quicklook Preview Item @interface PreviewItem : NSObject @property(readonly, nonatomic) NSURL *previewItemURL; @@ -43,7 +44,7 @@ //QuickLook Datasource for rending PDF docs @interface FileDataSource : NSObject -@property (strong, nonatomic) PreviewItem *item; +@property NSMutableArray *files; @end @interface ChatConversationView @@ -81,14 +82,12 @@ @property(weak, nonatomic) IBOutlet UIBackToCallButton *backToCallButton; @property (weak, nonatomic) IBOutlet UIButton *infoButton; @property (weak, nonatomic) IBOutlet UILabel *particpantsLabel; -//@property (nonatomic, strong) UIDocumentInteractionController *documentInteractionController; -@property NSMutableArray *imagesArray; -@property NSMutableArray *assetIdsArray; @property NSMutableArray *qualitySettingsArray; @property (weak, nonatomic) IBOutlet UICollectionView *imagesCollectionView; @property (weak, nonatomic) IBOutlet UIView *imagesView; @property (weak, nonatomic) IBOutlet UIButton *encryptedButton; @property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *toggleSelectionButton; +@property FileContext *fileContext; + (void)markAsRead:(LinphoneChatRoom *)chatRoom; + (void)autoDownload:(LinphoneChatMessage *)message; @@ -97,6 +96,8 @@ + (void)writeFileInCache:(NSData *)data name:(NSString *)name; + (NSData *)getCacheFileData:(NSString *)name; + (void)writeMediaToGallery:(NSString *)name fileType:(NSString *)fileType; ++(UIImage *)getBasicImage; ++(UIImage*)drawText:(NSString*)text image:(UIImage *)image textSize:(CGFloat)textSize; - (void)configureForRoom:(BOOL)editing; - (IBAction)onBackClick:(id)event; @@ -111,6 +112,7 @@ - (IBAction)onEncryptedDevicesClick:(id)sender; - (void)update; - (void)openFileWithURL:(NSURL *)url; +- (void)openFileWithURLs:(NSMutableArray*)urls index:(NSInteger)currentIndex; - (void)clearMessageView; - (void)configureMessageField; diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index 51b0ab6af..8c3b5a1d3 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -27,6 +27,50 @@ #import "DevicesListView.h" #import "SVProgressHUD.h" +@implementation FileContext + +- (void)addObject:(UIImage *)image withQuality:(float)quality { + NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; + NSData *data = UIImageJPEGRepresentation(image, quality); + + [self addObject:data name:name type:@"image" image:image]; +} + +- (void)addObject:(NSData *)data name:(NSString *)name type:(NSString *)type image:(UIImage *)image { + [_previewsArray addObject:image]; + [_uuidsArray addObject:[NSUUID UUID]]; + [self addObject:data name:name type:type]; +} + +- (void)addObject:(NSData *)data name:(NSString *)name type:(NSString *)type { + [_namesArray addObject:name]; + [_typesArray addObject:type]; + [_datasArray addObject:data]; +} + +- (void)deleteContentWithUuid:(NSUUID *)uuid { + NSUInteger key = [_uuidsArray indexOfObject:uuid]; + [_previewsArray removeObjectAtIndex:key]; + [_uuidsArray removeObjectAtIndex:key]; + [_namesArray removeObjectAtIndex:key]; + [_typesArray removeObjectAtIndex:key]; + [_datasArray removeObjectAtIndex:key]; +} + +- (void)clear { + _previewsArray = [NSMutableArray array]; + _uuidsArray = [NSMutableArray array]; + _namesArray = [NSMutableArray array]; + _typesArray = [NSMutableArray array]; + _datasArray = [NSMutableArray array]; +} + +- (NSUInteger)count { + return [_datasArray count]; +} + +@end + @implementation PreviewItem - (instancetype)initPreviewURL:(NSURL *)docURL @@ -41,18 +85,19 @@ @end @implementation FileDataSource -- (instancetype)initWithPreviewItem:(PreviewItem *)item { +- (instancetype)initWithFiles:(NSMutableArray*)files { self = [super init]; if (self) { - _item = item; + _files = files; } return self; } - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller { - return 1; + return _files.count; } - (id)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index { - return self.item; + NSURL *url = [_files objectAtIndex:index]; + return [[PreviewItem alloc] initPreviewURL:url WithTitle:[url lastPathComponent]]; } @end @@ -175,7 +220,7 @@ static UICompositeViewDescription *compositeDescription = nil; selector:@selector(onLinphoneCoreReady:) name:kLinphoneGlobalStateUpdate object:nil]; - if ([_imagesArray count] > 0) { + if ([_fileContext count] > 0) { [UIView animateWithDuration:0 delay:0 options:UIViewAnimationOptionBeginFromCurrentState @@ -419,11 +464,9 @@ static UICompositeViewDescription *compositeDescription = nil; } - (void)saveAndSend:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality{ - - [_imagesArray addObject:image]; - [_assetIdsArray addObject:phAssetId]; - [_qualitySettingsArray addObject:@(quality)]; - [self refreshImageDrawer]; + [_fileContext addObject:image withQuality:quality]; + [_qualitySettingsArray addObject:@(quality)]; + [self refreshImageDrawer]; } - (void)chooseImageQuality:(UIImage *)image assetId:(NSString *)phAssetId { @@ -583,7 +626,7 @@ static UICompositeViewDescription *compositeDescription = nil; messageRect.size.height += diff; [_messageView setFrame:messageRect]; - if ([_imagesArray count] > 0) { + if ([_fileContext count] > 0) { CGRect _imagesRect = [_imagesView frame]; _imagesRect.origin.y -= diff; [_imagesView setFrame:_imagesRect]; @@ -626,24 +669,28 @@ static UICompositeViewDescription *compositeDescription = nil; } - (IBAction)onSendClick:(id)event { - if ([_imagesArray count] > 0) { - int i = 0; - for (i = 0; i < [_imagesArray count] - 1; ++i) { - [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL]; - } - if (isOneToOne) { - [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL]; - if (![[self.messageField text] isEqualToString:@""]) { - [self sendMessage:[_messageField text] withExterlBodyUrl:nil]; - } - } else { - [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]]; - } - - [self clearMessageView]; - return; - } - [self sendMessageInMessageField]; + if ([_fileContext count] > 0) { + if (linphone_chat_room_get_capabilities(_chatRoom) & LinphoneChatRoomCapabilitiesConference) { + [self startMultiFilesUpload]; + } else { + int i = 0; + for (i = 0; i < [_fileContext count]-1; ++i) { + [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL]; + } + if (isOneToOne) { + [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:NULL]; + if (![[self.messageField text] isEqualToString:@""]) { + [self sendMessage:[_messageField text] withExterlBodyUrl:nil]; + } + } else { + [self startUploadData:[_fileContext.datasArray objectAtIndex:i] withType:[_fileContext.typesArray objectAtIndex:i] withName:[_fileContext.namesArray objectAtIndex:i] andMessage:[self.messageField text]]; + } + } + + [self clearMessageView]; + return; + } + [self sendMessageInMessageField]; } - (IBAction)onListTap:(id)sender { @@ -739,11 +786,25 @@ static UICompositeViewDescription *compositeDescription = nil; #pragma mark ChatRoomDelegate -- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message { +- (BOOL)startMultiFilesUpload { + FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; + [fileTransfer setText:[self.messageField text]]; + [fileTransfer uploadFileContent:_fileContext forChatRoom:_chatRoom]; + [_tableController scrollToBottom:true]; + return TRUE; +} + +- (BOOL)startUploadData:(NSData *)data withType:(NSString*)type withName:(NSString *)name andMessage:(NSString *)message { FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; if (message) [fileTransfer setText:message]; - [fileTransfer uploadImage:image forChatRoom:_chatRoom withQuality:quality]; + NSString *key = @"localfile"; + if ([type isEqualToString:@"video"]) { + key = @"localvideo"; + } else if ([type isEqualToString:@"image"]) { + key = @"localimage"; + } + [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key]; [_tableController scrollToBottom:true]; return TRUE; } @@ -755,6 +816,15 @@ static UICompositeViewDescription *compositeDescription = nil; return TRUE; } +- (BOOL)resendMultiFiles:(FileContext *)newFileContext message:(NSString *)message { + FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; + if (message) + [fileTransfer setText:message]; + [fileTransfer uploadFileContent:newFileContext forChatRoom:_chatRoom]; + [_tableController scrollToBottom:true]; + return TRUE; +} + - (BOOL)resendFile: (NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message { FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; if (message) @@ -805,7 +875,9 @@ static UICompositeViewDescription *compositeDescription = nil; [exportSession exportAsynchronouslyWithCompletionHandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ [SVProgressHUD dismiss]; - [self startFileUpload:[NSData dataWithContentsOfURL:compressedVideoUrl] withName:localname]; + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:compressedVideoUrl]; + [_fileContext addObject:[NSData dataWithContentsOfURL:compressedVideoUrl] name:localname type:@"video" image:image]; + [self refreshImageDrawer]; }); }]; @@ -1005,7 +1077,7 @@ static UICompositeViewDescription *compositeDescription = nil; } - if ([_imagesArray count] > 0){ + if ([_fileContext count] > 0){ // resizing imagesView CGRect imagesFrame = [_imagesView frame]; imagesFrame.origin.y = [_messageView frame].origin.y - heightDiff; @@ -1074,7 +1146,7 @@ static UICompositeViewDescription *compositeDescription = nil; [_tableController.view setFrame:tableFrame]; } - if ([_imagesArray count] > 0){ + if ([_fileContext count] > 0){ // resizing imagesView CGRect imagesFrame = [_imagesView frame]; imagesFrame.origin.y = [_messageView frame].origin.y - heightDiff; @@ -1213,21 +1285,24 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog [view.encryptedButton setImage:image forState:UIControlStateNormal]; } -- (void)openFileWithURL:(NSURL *)url +- (void)openFileWithURLs:(NSMutableArray*)urls index:(NSInteger)currentIndex { //create the Quicklook controller. QLPreviewController *qlController = [[QLPreviewController alloc] init]; - - PreviewItem *item = [[PreviewItem alloc] initPreviewURL:url WithTitle:[url lastPathComponent]]; - self.FileDataSource = [[FileDataSource alloc] initWithPreviewItem:item]; + self.FileDataSource = [[FileDataSource alloc] initWithFiles:urls]; qlController.dataSource = self.FileDataSource; + qlController.currentPreviewItemIndex = currentIndex; qlController.delegate = self; //present the document. [self presentViewController:qlController animated:YES completion:nil]; } +- (void)openFileWithURL:(NSURL *)url +{ + [self openFileWithURLs:[NSMutableArray arrayWithObject:url] index:0]; +} - (void)previewControllerDidDismiss:(QLPreviewController *)controller { @@ -1307,23 +1382,21 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog } } -- (void)deleteImageWithAssetId:(NSString *)assetId { - NSUInteger key = [_assetIdsArray indexOfObject:assetId]; - [_imagesArray removeObjectAtIndex:key]; - [_assetIdsArray removeObjectAtIndex:key]; - [self refreshImageDrawer]; +- (void)deleteFileWithUuid:(NSUUID *)uuid { + [_fileContext deleteContentWithUuid:uuid]; + [self refreshImageDrawer]; } - (void)clearMessageView { [_messageField setText:@""]; - _imagesArray = [NSMutableArray array]; - _assetIdsArray = [NSMutableArray array]; + if (!_fileContext) _fileContext = [[FileContext alloc] init]; + [_fileContext clear]; [self refreshImageDrawer]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [_imagesArray count]; + return [_fileContext count]; } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -1336,8 +1409,8 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog imgFrame.size.height = 100; } - [imgView.image setImage:[UIImage resizeImage:[_imagesArray objectAtIndex:[indexPath item]] withMaxWidth:imgFrame.size.width andMaxHeight:imgFrame.size.height]]; - [imgView setAssetId:[_assetIdsArray objectAtIndex:[indexPath item]]]; + [imgView.image setImage:[UIImage resizeImage:[_fileContext.previewsArray objectAtIndex:[indexPath item]] withMaxWidth:imgFrame.size.width andMaxHeight:imgFrame.size.height]]; + [imgView setUuid:[_fileContext.uuidsArray objectAtIndex:[indexPath item]]]; [imgView setDeleteDelegate:self]; [imgView setFrame:imgFrame]; [_sendButton setEnabled:TRUE]; @@ -1347,7 +1420,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog - (void)refreshImageDrawer { int heightDiff = UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) ? 55 : 105; - if ([_imagesArray count] == 0) { + if ([_fileContext count] == 0) { [UIView animateWithDuration:0 delay:0 options:UIViewAnimationOptionBeginFromCurrentState @@ -1433,10 +1506,38 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog NSFileCoordinator *co =[[NSFileCoordinator alloc] init]; NSError *error = nil; [co coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL * _Nonnull newURL) { - [self startFileUpload:[NSData dataWithContentsOfURL:newURL] withName:[newURL lastPathComponent]]; + UIImage *image = [ChatConversationView drawText:[newURL lastPathComponent] image:[ChatConversationView getBasicImage] textSize:10]; + [_fileContext addObject:[NSData dataWithContentsOfURL:newURL] name:[newURL lastPathComponent] type:@"file" image:image]; + [self refreshImageDrawer]; }]; [url stopAccessingSecurityScopedResource]; } ++(UIImage *)getBasicImage { + UIColor *color=[UIColor grayColor]; + CGRect frame = CGRectMake(0, 0, 200, 200); + UIGraphicsBeginImageContext(frame.size); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, [color CGColor]); + CGContextFillRect(context, frame); + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + ++(UIImage*)drawText:(NSString*)text image:(UIImage *)image textSize:(CGFloat)textSize +{ + UIFont *font = [UIFont boldSystemFontOfSize:textSize]; + UIGraphicsBeginImageContext(image.size); + [image drawInRect:CGRectMake(0,0,image.size.width,image.size.height)]; + CGRect rect = CGRectMake(0, 30, image.size.width, image.size.height); + [[UIColor whiteColor] set]; + [text drawInRect:CGRectIntegral(rect) withFont:font]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return newImage; +} + @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index 69ab9281b..71d45bcfb 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -24,6 +24,7 @@ #import "FileTransferDelegate.h" #import "ChatConversationTableView.h" #import "UIChatBubbleTextCell.h" +#import "UIChatContentView.h" @interface UIChatBubblePhotoCell : UIChatBubbleTextCell @@ -41,8 +42,10 @@ @property (weak, nonatomic) IBOutlet UIButton *fileButton; @property (weak, nonatomic) IBOutlet UIView *fileView; @property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer; +@property(strong, nonatomic) NSMutableArray *contentViews; -- (void)setEvent:(LinphoneEventLog *)event; + +- (void)setEvent:(LinphoneEventLog *)event vfsEnabled:(BOOL)enabled; - (void)setChatMessage:(LinphoneChatMessage *)message; - (void)connectToFileDelegate:(FileTransferDelegate *)ftd; - (IBAction)onDownloadClick:(id)event; diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 6ce7def06..3c3e13e6e 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -53,29 +53,21 @@ videoDefaultSize = CGSizeMake(320, 240); assetIsLoaded = FALSE; self.contentView.userInteractionEnabled = NO; + _contentViews = [[NSMutableArray alloc] init]; } return self; } -- (void)dealloc { - NSString *filePath = [LinphoneManager getMessageAppDataForKey:@"encryptedfile" inMessage:self.message]; - if (filePath) { - [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL]; - [LinphoneManager setValueInMessageAppData:NULL forKey:@"encryptedfile" inMessage:self.message]; - } -} - - - (void)onDelete { [super onDelete]; } #pragma mark - -- (void)setEvent:(LinphoneEventLog *)event { +- (void)setEvent:(LinphoneEventLog *)event vfsEnabled:(BOOL)enabled { if (!event || !(linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage)) return; - super.event = event; + [super setEvent:event vfsEnabled:enabled]; [self setChatMessage:linphone_event_log_get_chat_message(event)]; } @@ -101,7 +93,6 @@ } [super setChatMessageForCbs:amessage]; - [LinphoneManager setValueInMessageAppData:NULL forKey:@"encryptedfile" inMessage:self.message]; } - (void) loadImageAsset:(PHAsset*) asset image:(UIImage *)image { @@ -117,8 +108,6 @@ }); } -static const CGFloat CELL_IMAGE_X_MARGIN = 100; - - (void) loadAsset:(PHAsset *) asset { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; options.synchronous = TRUE; @@ -165,6 +154,59 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; return; } [super update]; + + const bctbx_list_t *contents = linphone_chat_message_get_contents(self.message); + BOOL multiParts = ((linphone_chat_message_get_text_content(self.message) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1); + if (multiParts) { + if (!assetIsLoaded) { + NSMutableDictionary *encrptedFilePaths = NULL; + if (self.vfsEnabled) { + encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:self.message]; + if (!encrptedFilePaths) { + encrptedFilePaths = [NSMutableDictionary dictionary]; + } + } + + _imageGestureRecognizer.enabled = NO; + _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; + + const bctbx_list_t *it = contents; + int i; + for (it = contents, i=0; it != NULL; it=bctbx_list_next(it)){ + LinphoneContent *content = (LinphoneContent *)it->data; + if (linphone_content_is_file_transfer(content) || linphone_content_is_file(content)){ + UIChatContentView *contentView = [[UIChatContentView alloc] initWithFrame: CGRectMake(0,0,0,0)]; + if(self.vfsEnabled && (linphone_chat_message_is_outgoing(self.message) || linphone_content_is_file(content))) { + // downloaded or ougoing message + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + NSString *filePath = [encrptedFilePaths valueForKey:name]; + if (filePath == NULL) { + char *cPath = linphone_content_get_plain_file_path(content); + if (cPath) { + NSString *filePath = [NSString stringWithUTF8String:cPath]; + ms_free(cPath); + [encrptedFilePaths setValue:filePath forKey:name]; + } + } + contentView.filePath = filePath; + } + [contentView setContent:content message:self.message]; + contentView.position = i; + [_contentViews addObject:contentView]; + i++; + } + } + if (self.vfsEnabled) { + [LinphoneManager setValueInMessageAppData:encrptedFilePaths forKey:@"encryptedfiles" inMessage:self.message]; + } + assetIsLoaded = TRUE; + [self layoutSubviews]; + } + return; + } + + + const char *url = linphone_chat_message_get_external_body_url(self.message); BOOL is_external = (url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message); @@ -203,11 +245,13 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; NSString *fileName = [NSString stringWithUTF8String:linphone_content_get_name(fileContent)]; if (!filePath) { - char *cPath = [[LinphoneManager instance] lpConfigBoolForKey:@"vfs_enabled_preference"] ? linphone_content_get_plain_file_path(fileContent) : NULL; - if (cPath) { - filePath = [NSString stringWithUTF8String:cPath]; - ms_free(cPath); - [LinphoneManager setValueInMessageAppData:filePath forKey:@"encryptedfile" inMessage:self.message]; + if (self.vfsEnabled) { + char *cPath = self.vfsEnabled ? linphone_content_get_plain_file_path(fileContent) : NULL; + if (cPath) { + filePath = [NSString stringWithUTF8String:cPath]; + ms_free(cPath); + [LinphoneManager setValueInMessageAppData:filePath forKey:@"encryptedfile" inMessage:self.message]; + } } else { filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:fileName]; } @@ -638,20 +682,60 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; bubbleFrame.origin.x = origin_x; super.bubbleView.frame = bubbleFrame; - - // Resizing Image view - if (_finalImage.image) { - CGRect imgFrame = self.finalAssetView.frame; - imgFrame.size = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[_finalImage.image size] withWidth:chatTableView.tableView.frame.size.width - CELL_IMAGE_X_MARGIN]; - imgFrame.origin.x = (self.innerView.frame.size.width - imgFrame.size.width-17)/2; - self.finalAssetView.frame = imgFrame; - } + + if (_contentViews.count > 0) { + // Positioning contentViews + CGFloat imagesw=0; + CGFloat max_imagesh=0; + CGFloat max_imagesw=0; + CGFloat originy=0; + CGFloat originx=0; + CGFloat availableWidth = chatTableView.tableView.frame.size.width-CELL_IMAGE_X_MARGIN; + + NSMutableArray *fileUrls = [[NSMutableArray alloc] init]; + for (UIChatContentView *contentView in _contentViews) { + if (contentView.filePath) { + [fileUrls addObject:[NSURL fileURLWithPath:contentView.filePath]]; + } + } + for (UIChatContentView *contentView in _contentViews) { + UIImage *image = contentView.image; + CGSize sSize = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:image.size withWidth:IMAGE_DEFAULT_WIDTH]; + imagesw += sSize.width; + if (imagesw > availableWidth) { + imagesw = sSize.width; + max_imagesw = MAX(max_imagesw, imagesw); + originy = max_imagesh+IMAGE_DEFAULT_MARGIN; + max_imagesh = sSize.height; + originx = sSize.width; + } else { + max_imagesw = MAX(max_imagesw, imagesw); + max_imagesh = MAX(max_imagesh, sSize.height); + originx += (sSize.width+IMAGE_DEFAULT_MARGIN); + } + + [contentView setFrame:CGRectMake(originx-sSize.width, originy, sSize.width, sSize.height)]; + contentView.fileUrls = fileUrls; + [_finalAssetView addSubview:contentView]; + } + _finalImage.hidden = YES; + } else { + // Resizing Image view + if (_finalImage.image) { + CGRect imgFrame = self.finalAssetView.frame; + imgFrame.size = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[_finalImage.image size] withWidth:chatTableView.tableView.frame.size.width - CELL_IMAGE_X_MARGIN]; + imgFrame.origin.x = (self.innerView.frame.size.width - imgFrame.size.width-17)/2; + self.finalAssetView.frame = imgFrame; + } + } // Positioning text message const char *utf8Text = linphone_chat_message_get_text_content(self.message); CGRect textFrame = self.messageText.frame; - if (_finalImage.image) + if (_contentViews.count > 0) + textFrame.origin = CGPointMake(textFrame.origin.x, self.finalAssetView.frame.origin.y + self.finalAssetView.frame.size.height-10); + else if (_finalImage.image) textFrame.origin = CGPointMake(textFrame.origin.x, self.finalAssetView.frame.origin.y + self.finalAssetView.frame.size.height); else // When image hasn't be download diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h index 2bf4c248b..ba3f981dd 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.h +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -23,6 +23,10 @@ #import "ChatConversationTableView.h" #import "UIRoundedImageView.h" +#define CELL_IMAGE_X_MARGIN 100 +#define IMAGE_DEFAULT_WIDTH 120 +#define IMAGE_DEFAULT_MARGIN 5 + @interface UIChatBubbleTextCell : UITableViewCell @property(readonly, nonatomic) LinphoneEventLog *event; @@ -42,17 +46,20 @@ @property (nonatomic, strong) UIDocumentPickerViewController *documentPicker; @property (weak, nonatomic) IBOutlet UIView *innerView; -@property(nonatomic) Boolean isFirst; -@property(nonatomic) Boolean isLast; -@property(nonatomic) Boolean notDelivered; +@property(nonatomic) BOOL isFirst; +@property(nonatomic) BOOL isLast; +@property(nonatomic) BOOL notDelivered; +@property(nonatomic) BOOL vfsEnabled; + (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width; + (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText; + (CGSize)getMediaMessageSizefromOriginalSize:(CGSize)originalSize withWidth:(int)width; + (UIImage *)getImageFromVideoUrl:(NSURL *)url; ++ (UIImage *)getImageFromContent:(LinphoneContent *)content filePath:(NSString *)filePath; -- (void)setEvent:(LinphoneEventLog *)event; +- (void)setEvent:(LinphoneEventLog *)event vfsEnabled:(BOOL)enabled; - (void)setChatMessageForCbs:(LinphoneChatMessage *)message; +- (void)clearEncryptedFiles; - (void)onDelete; - (void)onResend; diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 42d35b259..43491de4a 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -62,17 +62,38 @@ } - (void)dealloc { - [self setEvent:NULL]; + [self setEvent:NULL vfsEnabled:_vfsEnabled]; [self setChatMessageForCbs:NULL]; } #pragma mark - -- (void)setEvent:(LinphoneEventLog *)event { +- (void)clearEncryptedFiles { + if (_vfsEnabled) { + NSMutableDictionary *encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:_message]; + if ([encrptedFilePaths count] > 0) { + for(NSString *path in [encrptedFilePaths allValues]) { + [[NSFileManager defaultManager] removeItemAtPath:path error:NULL]; + } + [LinphoneManager setValueInMessageAppData:NULL forKey:@"encryptedfiles" inMessage:_message]; + return; + } + + NSString *filePath = [LinphoneManager getMessageAppDataForKey:@"encryptedfile" inMessage:_message]; + if (filePath) { + if (![filePath isEqualToString:@""]) + [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL]; + [LinphoneManager setValueInMessageAppData:NULL forKey:@"encryptedfile" inMessage:_message]; + } + } +} + +- (void)setEvent:(LinphoneEventLog *)event vfsEnabled:(BOOL)enabled { if(!event) return; _event = event; + _vfsEnabled = enabled; if (!(linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage)) { LOGE(@"Impossible to create a ChatBubbleText whit a non message event"); return; @@ -274,33 +295,52 @@ if (state != LinphoneChatMessageStateNotDelivered && state != LinphoneChatMessageStateFileTransferError) return; + const bctbx_list_t *contents = linphone_chat_message_get_contents(_message); + BOOL multiParts = ((linphone_chat_message_get_text_content(self.message) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1); + if (multiParts) { + FileContext *newfileContext = [[FileContext alloc] init]; + [newfileContext clear]; + NSMutableDictionary *encrptedFilePaths = encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:_message]; + int i; + const bctbx_list_t *it; + for (it = contents, i=0; it != NULL; it=bctbx_list_next(it)){ + LinphoneContent *content = (LinphoneContent *)it->data; + if (linphone_content_is_file_transfer(content) || linphone_content_is_file(content)){ + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + NSString *filePath = [encrptedFilePaths valueForKey:name]; + if (filePath == NULL) { + filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + } + [newfileContext addObject:[NSData dataWithContentsOfFile:filePath] name:name type:[NSString stringWithUTF8String:linphone_content_get_type(content)]]; + } + } + [self onDelete]; + dispatch_async(dispatch_get_main_queue(), ^ { + const char *text = linphone_chat_message_get_text_content(_message); + [_chatRoomDelegate resendMultiFiles:newfileContext message: text? [NSString stringWithUTF8String:text]: NULL]; + }); + return; + } if (linphone_chat_message_get_file_transfer_information(_message) != NULL) { NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message]; NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message]; NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message]; - - /*FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:_message]; - if (thiz) { - [thiz stop] - }*/ + NSString *filePath = [LinphoneManager getMessageAppDataForKey:@"encryptedfile" inMessage:self.message]; + [self onDelete]; dispatch_async(dispatch_get_main_queue(), ^ { - LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(_message); NSData *data = NULL; - char *cPath = [[LinphoneManager instance] lpConfigBoolForKey:@"vfs_enabled_preference"] ? linphone_content_get_plain_file_path(fileContent) : NULL; - if (cPath) { - NSString *filePath = [NSString stringWithUTF8String:cPath]; + if (filePath) { data = [NSData dataWithContentsOfFile:filePath]; - ms_free(cPath); - [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL]; } - + const char *text = linphone_chat_message_get_text_content(_message); + NSString *str = text ? [NSString stringWithUTF8String:text] : NULL; if (localImage) { - [_chatRoomDelegate resendFile: (data?:[ChatConversationView getCacheFileData:localImage]) withName:localImage type:@"image" key:@"localimage" message:self.textMessage]; + [_chatRoomDelegate resendFile: (data?:[ChatConversationView getCacheFileData:localImage]) withName:localImage type:@"image" key:@"localimage" message:str]; } else if (localVideo) { - [_chatRoomDelegate resendFile:(data?:[ChatConversationView getCacheFileData:localVideo]) withName:localVideo type:@"video" key:@"localvideo" message:self.textMessage]; + [_chatRoomDelegate resendFile:(data?:[ChatConversationView getCacheFileData:localVideo]) withName:localVideo type:@"video" key:@"localvideo" message:str]; } else { - [_chatRoomDelegate resendFile:(data?:[ChatConversationView getCacheFileData:localFile]) withName:localFile type:@"image" key:@"localfile" message:self.textMessage]; + [_chatRoomDelegate resendFile:(data?:[ChatConversationView getCacheFileData:localFile]) withName:localFile type:@"image" key:@"localfile" message:str]; } }); } else { @@ -364,7 +404,6 @@ static const CGFloat CELL_MIN_HEIGHT = 65.0f; static const CGFloat CELL_MIN_WIDTH = 190.0f; static const CGFloat CELL_MESSAGE_X_MARGIN = 68 + 10.0f; static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; -static const CGFloat CELL_IMAGE_X_MARGIN = 100; + (CGSize)ViewHeightForMessage:(LinphoneChatMessage *)chat withWidth:(int)width { return [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil]; @@ -378,7 +417,28 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; return size; } -+ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText{ ++ (UIImage *)getImageFromContent:(LinphoneContent *)content filePath:(NSString *)filePath; { + NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(content)]; + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + if (!filePath) { + filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + } + + UIImage *image = nil; + if ([type isEqualToString:@"video"]) { + image = [UIChatBubbleTextCell getImageFromVideoUrl:[NSURL fileURLWithPath:filePath]]; + } else if ([type isEqualToString:@"image"]) { + NSData* data = [NSData dataWithContentsOfFile:filePath]; + image = [[UIImage alloc] initWithData:data]; + } + if (image) return image; + UIImage *basicImage = [ChatConversationView getBasicImage]; + image = [ChatConversationView drawText:[NSString stringWithFormat:@"📎 %@",name] image:basicImage textSize:25]; + return image; +} + + ++ (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText { NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat]; static UIFont *messageFont = nil; @@ -399,7 +459,68 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN + 50, CELL_MIN_HEIGHT); return size; } - + + CGFloat imagesw=0; + CGFloat imagesh=0; + CGFloat max_imagesw=0; + CGFloat max_imagesh=0; + const bctbx_list_t *contents = linphone_chat_message_get_contents(chat); + BOOL multiParts = ((linphone_chat_message_get_text_content(chat) != NULL) ? bctbx_list_size(contents) > 2 : bctbx_list_size(contents) > 1); + if (multiParts) { + const bctbx_list_t *it = contents; + NSMutableDictionary *encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:chat]; + + for (it = contents; it != NULL; it=bctbx_list_next(it)){ + LinphoneContent *content = (LinphoneContent *)it->data; + UIImage *image; + if(!linphone_chat_message_is_outgoing(chat) && linphone_content_is_file_transfer(content)) { + // not yet downloaded + UIImage *basicImage = [ChatConversationView getBasicImage]; + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)] ; + image = [ChatConversationView drawText:name image:basicImage textSize:25]; + } else if (linphone_content_is_file_transfer(content) || linphone_content_is_file(content)) { + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + NSString *filePath=[encrptedFilePaths valueForKey:name]; + if (filePath == NULL) { + filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + } + + image = [UIChatBubbleTextCell getImageFromContent:content filePath:filePath]; + } + if (image) { + CGSize sSize = [self getMediaMessageSizefromOriginalSize:image.size withWidth:IMAGE_DEFAULT_WIDTH]; + imagesw += sSize.width; + if (imagesw > width) { + imagesw = sSize.width; + max_imagesw = MAX(max_imagesw, imagesw); + max_imagesh = imagesh; + imagesh = sSize.height; + } else { + max_imagesw = MAX(max_imagesw, imagesw); + imagesh = MAX(imagesh, sSize.height); + } + } + } + + max_imagesh += imagesh; + size = CGSizeMake(max_imagesw, max_imagesh); + CGSize textSize = CGSizeMake(0, 0); + if (![messageText isEqualToString:@"🗻"]) { + textSize = [self computeBoundingBox:messageText + size:CGSizeMake(width - CELL_MESSAGE_X_MARGIN - 4, CGFLOAT_MAX) + font:messageFont]; + size.height += textSize.height; + } + + // add size for message text + size.height += textSize.height; + size.width = MAX(textSize.width, size.width); + size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH); + size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT); + return size; + } + + LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(chat); if (url == nil && fileContent == NULL) { size = [self computeBoundingBox:messageText @@ -410,6 +531,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:chat]; NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:chat]; NSString *filePath = [LinphoneManager getMessageAppDataForKey:@"encryptedfile" inMessage:chat]; + NSString *fileName = [NSString stringWithUTF8String:linphone_content_get_name(fileContent)]; CGSize textSize = CGSizeMake(0, 0); if (![messageText isEqualToString:@"🗻"]) { @@ -420,12 +542,13 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; } CGSize originalImageSize = CGSizeMake(230, 50); + if (!filePath) { + filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:fileName]; + } if (localFile) { UIImage *image = nil; NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)]; - if (!filePath) { - filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]; - } + if ([type isEqualToString:@"video"]) { if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) { image = [self getImageFromVideoUrl:[NSURL fileURLWithPath:filePath]]; @@ -451,13 +574,6 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20); } - if (!filePath) { - if (localImage) { - filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]; - } else { - filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]; - } - } if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:filePath]) { NSData* data = [NSData dataWithContentsOfFile:filePath]; UIImage *image = [[UIImage alloc] initWithData:data]; diff --git a/Classes/LinphoneUI/UIChatContentView.h b/Classes/LinphoneUI/UIChatContentView.h new file mode 100644 index 000000000..9851ca3a9 --- /dev/null +++ b/Classes/LinphoneUI/UIChatContentView.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#import + + +@interface UIChatContentView : UIImageView + +@property(strong, nonatomic) NSMutableArray *fileUrls; +@property(nonatomic) NSInteger position; +@property(readonly, nonatomic) LinphoneContent *content; +@property(readonly, nonatomic) LinphoneChatMessage *message; +@property(nonatomic) UIButton *downloadButton; +@property(nonatomic) NSString *filePath; + +- (void)setContent:(LinphoneContent *)content message:(LinphoneChatMessage *)message; + +@end diff --git a/Classes/LinphoneUI/UIChatContentView.m b/Classes/LinphoneUI/UIChatContentView.m new file mode 100644 index 000000000..fd24709cf --- /dev/null +++ b/Classes/LinphoneUI/UIChatContentView.m @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#import +#import "UIChatContentView.h" +#import "ChatConversationView.h" +#import "PhoneMainView.h" + +@implementation UIChatContentView + +- (void)setContent:(LinphoneContent *)content message:(LinphoneChatMessage *)message { + _content = content; + _message = message; + self.userInteractionEnabled = YES; + + if(!linphone_chat_message_is_outgoing(_message) && linphone_content_is_file_transfer(_content)) { + // has not yet downloaded + UIImage *basicImage = [ChatConversationView getBasicImage]; + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)] ; + UIImage *image = [ChatConversationView drawText:name image:basicImage textSize:25]; + [self setImage:image]; + _downloadButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_downloadButton addTarget:self + action:@selector(onDownloadClick:) + forControlEvents:UIControlEventTouchUpInside]; + _downloadButton.backgroundColor = [UIColor orangeColor]; + UIFont *boldFont = [UIFont systemFontOfSize:10]; + NSMutableAttributedString *boldText = [[NSMutableAttributedString alloc] initWithString:@"Download" attributes:@{ NSFontAttributeName : boldFont }]; + [_downloadButton setAttributedTitle:boldText forState:UIControlStateNormal]; + _downloadButton.frame = CGRectMake(3, 3, 60, 30); + [self addSubview:_downloadButton]; + } else { + if (_filePath == NULL) { + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + _filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + } + UIImage *image = [UIChatBubbleTextCell getImageFromContent:content filePath:_filePath]; + [self setImage:image]; + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onMultiPartClick:)]; + tapGestureRecognizer.numberOfTapsRequired = 1; + tapGestureRecognizer.enabled = YES; + [self addGestureRecognizer:tapGestureRecognizer]; + } +} + + +-(IBAction)onMultiPartClick:(id)sender { + ChatConversationView *view = VIEW(ChatConversationView); + [view openFileWithURLs:_fileUrls index:_position]; +} + +-(IBAction)onDownloadClick:(id)sender { + _downloadButton.enabled = NO; + linphone_content_set_file_path(_content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:[NSString stringWithUTF8String:linphone_content_get_name(_content)]].UTF8String); + linphone_chat_message_download_content(_message, _content); +} + +@end + diff --git a/Classes/LinphoneUI/UIImageViewDeletable.h b/Classes/LinphoneUI/UIImageViewDeletable.h index d2453607c..5048b8303 100644 --- a/Classes/LinphoneUI/UIImageViewDeletable.h +++ b/Classes/LinphoneUI/UIImageViewDeletable.h @@ -23,13 +23,13 @@ @required -- (void)deleteImageWithAssetId:(NSString *)assetId; +- (void)deleteFileWithUuid:(NSUUID *)uuid; @end @interface UIImageViewDeletable : UICollectionViewCell -@property NSString *assetId; +@property NSUUID *uuid; @property(nonatomic, strong) id deleteDelegate; @property (weak, nonatomic) IBOutlet UIImageView *image; diff --git a/Classes/LinphoneUI/UIImageViewDeletable.m b/Classes/LinphoneUI/UIImageViewDeletable.m index 3558e8599..aa8294c31 100644 --- a/Classes/LinphoneUI/UIImageViewDeletable.m +++ b/Classes/LinphoneUI/UIImageViewDeletable.m @@ -55,7 +55,7 @@ } - (IBAction)onDeletePressed { - [_deleteDelegate deleteImageWithAssetId:_assetId]; + [_deleteDelegate deleteFileWithUuid:_uuid]; } /* diff --git a/Classes/Utils/FileTransferDelegate.h b/Classes/Utils/FileTransferDelegate.h index e2267cae0..3aa3d22db 100644 --- a/Classes/Utils/FileTransferDelegate.h +++ b/Classes/Utils/FileTransferDelegate.h @@ -20,9 +20,11 @@ #import #import "LinphoneManager.h" +#import "ChatConversationView.h" @interface FileTransferDelegate : NSObject +- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom; - (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key; - (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality; - (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name; diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index a066e4b83..de7c259ee 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -108,6 +108,7 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, return; } [LinphoneManager.instance.fileTransferDelegates addObject:self]; + [ChatConversationView writeFileInCache:data name:name]; LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); linphone_content_set_type(content, [type UTF8String]); @@ -128,21 +129,56 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, linphone_chat_message_send(_message); } +- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom { + [LinphoneManager.instance.fileTransferDelegates addObject:self]; + + _message = linphone_chat_room_create_empty_message(chatRoom); + NSMutableArray *names = [[NSMutableArray alloc] init]; + NSMutableArray *types = [[NSMutableArray alloc] init]; + + int i = 0; + for (i = 0; i < [context count]; ++i) { + LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); + NSString *type = [context.typesArray objectAtIndex:i]; + NSString *name = [context.namesArray objectAtIndex:i]; + NSData *data = [context.datasArray objectAtIndex:i]; + + [ChatConversationView writeFileInCache:data name:name]; + + linphone_content_set_type(content, [type UTF8String]); + + linphone_content_set_subtype(content, [name.pathExtension UTF8String]); + linphone_content_set_name(content, [name UTF8String]); + linphone_content_set_file_path(content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name].UTF8String); + [names addObject:name]; + [types addObject:type]; + linphone_chat_message_add_file_content(_message, content); + linphone_content_unref(content); + } + + if (_text!=nil && ![_text isEqualToString:@""]) + linphone_chat_message_add_text_content(_message, [_text UTF8String]); + + // todo indication progress + [LinphoneManager setValueInMessageAppData:names forKey:@"multiparts" inMessage:_message]; + [LinphoneManager setValueInMessageAppData:types forKey:@"multipartstypes" inMessage:_message]; + LOGI(@"%p Uploading content from message %p", self, _message); + linphone_chat_message_send(_message); +} + + - (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality { NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; NSData *data = UIImageJPEGRepresentation(image, quality); - [ChatConversationView writeFileInCache:data name:name]; [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage"]; } - (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom { NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; - [ChatConversationView writeFileInCache:data name:name]; [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo"]; } - (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name { - [ChatConversationView writeFileInCache:data name:name]; NSURL *url = [ChatConversationView getCacheFileUrl:name]; AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file"; diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 8ddaabba3..03cf8b019 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -104,6 +104,7 @@ 615A28422180C0870060F920 /* recording.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28412180C0820060F920 /* recording.png */; }; 615A28442180C0900060F920 /* recording@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28432180C08F0060F920 /* recording@2x.png */; }; 617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 617B4A62260A2B7800A87337 /* RecordingsListView.xib */; }; + 617C242A263022690042FB4A /* UIChatContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 617C2429263022690042FB4A /* UIChatContentView.m */; }; 6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */; }; 61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; }; 61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; }; @@ -962,6 +963,8 @@ 617B4A61260A2B7800A87337 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/RecordingsListView.xib; sourceTree = ""; }; 617B4A64260A2B8500A87337 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/RecordingsListView.strings; sourceTree = ""; }; 617B4A75260A3F5500A87337 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/RecordingsListView.strings; sourceTree = ""; }; + 617C2428263022430042FB4A /* UIChatContentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIChatContentView.h; sourceTree = ""; }; + 617C2429263022690042FB4A /* UIChatContentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIChatContentView.m; sourceTree = ""; }; 6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; 6187B1B524B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AboutView.strings; sourceTree = ""; }; 6187B1B624B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantLinkView.strings; sourceTree = ""; }; @@ -2178,6 +2181,8 @@ D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleTextCell.h */, D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */, 639E9CA51C0DB7EA00019A75 /* UIChatBubbleTextCell.xib */, + 617C2428263022430042FB4A /* UIChatContentView.h */, + 617C2429263022690042FB4A /* UIChatContentView.m */, D3EA540F159853750037DC6B /* UIChatCell.h */, D3EA5410159853750037DC6B /* UIChatCell.m */, 639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */, @@ -4207,6 +4212,7 @@ 6377AC801BDE4069007F7625 /* UIBackToCallButton.m in Sources */, 6308F9C51BF0DD6600D1234B /* XMLRPCHelper.m in Sources */, D3ED3E871586291E006C0DE4 /* TabBarView.m in Sources */, + 617C242A263022690042FB4A /* UIChatContentView.m in Sources */, D3ED3EA71587334E006C0DE4 /* HistoryListTableView.m in Sources */, 61AEBEBD2191990A00F35E7F /* DevicesListView.m in Sources */, D3ED3EB81587392C006C0DE4 /* HistoryListView.m in Sources */,