diff --git a/Classes/Base.lproj/ChatConversationImdnView.xib b/Classes/Base.lproj/ChatConversationImdnView.xib index c768ffd1d..99dc77538 100644 --- a/Classes/Base.lproj/ChatConversationImdnView.xib +++ b/Classes/Base.lproj/ChatConversationImdnView.xib @@ -1,17 +1,15 @@ - + + - + + + - - - - - @@ -19,19 +17,19 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + @@ -112,10 +76,14 @@ - - + + + + + + diff --git a/Classes/Base.lproj/ChatConversationView.xib b/Classes/Base.lproj/ChatConversationView.xib index e4db76e66..446b483da 100644 --- a/Classes/Base.lproj/ChatConversationView.xib +++ b/Classes/Base.lproj/ChatConversationView.xib @@ -31,6 +31,7 @@ + @@ -387,6 +388,11 @@ + diff --git a/Classes/LinphoneUI/Base.lproj/UIChatReplyBubbleView.xib b/Classes/LinphoneUI/Base.lproj/UIChatReplyBubbleView.xib new file mode 100644 index 000000000..4a657bdc5 --- /dev/null +++ b/Classes/LinphoneUI/Base.lproj/UIChatReplyBubbleView.xib @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Classes/LinphoneUI/Base.lproj/UIConfirmationDialog.xib b/Classes/LinphoneUI/Base.lproj/UIConfirmationDialog.xib index 3bc5b8c8e..eb265f061 100644 --- a/Classes/LinphoneUI/Base.lproj/UIConfirmationDialog.xib +++ b/Classes/LinphoneUI/Base.lproj/UIConfirmationDialog.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -15,6 +13,7 @@ + @@ -25,26 +24,26 @@ - + - + - - + - - + + - + + diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index c1530c413..49555feaa 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -41,7 +41,6 @@ @property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer; @property (weak, nonatomic) IBOutlet UIButton *fileButton; @property (weak, nonatomic) IBOutlet UIView *fileView; -@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer; @property(strong, nonatomic) NSMutableArray *contentViews; // Video recordings @@ -61,10 +60,8 @@ - (IBAction)onDownloadClick:(id)event; - (IBAction)onImageClick:(id)event; - (IBAction)onCancelClick:(id)sender; -- (IBAction)onResendClick:(id)event; - (IBAction)onPlayClick:(id)sender; - (IBAction)onFileClick:(id)sender; -- (IBAction)onPlusClick:(id)sender; @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 43474ea1a..f94ea00d7 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -61,8 +61,13 @@ assetIsLoaded = FALSE; self.contentView.userInteractionEnabled = NO; _contentViews = [[NSMutableArray alloc] init]; - self.vrWaveMaskPlayback.layer.cornerRadius = 10.0f; - self.vrWaveMaskPlayback.layer.masksToBounds = YES; + + + self.vrView.layer.cornerRadius = 30.0f; + self.vrView.layer.masksToBounds = YES; + [self.innerView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onPopupMenuPressed)]]; + self.messageText.userInteractionEnabled = false; + } return self; } @@ -82,7 +87,6 @@ - (void)setChatMessage:(LinphoneChatMessage *)amessage { _imageGestureRecognizer.enabled = NO; - _plusLongGestureRecognizer.enabled = NO; _messageImageView.image = nil; _finalImage.image = nil; _finalImage.hidden = TRUE; @@ -115,7 +119,6 @@ _messageImageView.hidden = YES; _finalImage.hidden = NO; _fileView.hidden = YES; - _plusLongGestureRecognizer.enabled = YES; [self layoutSubviews]; }); } @@ -151,7 +154,6 @@ [_messageImageView stopLoading]; _messageImageView.hidden = YES; _imageGestureRecognizer.enabled = YES; - _plusLongGestureRecognizer.enabled = YES; _finalImage.hidden = NO; [self layoutSubviews]; }); @@ -499,25 +501,6 @@ }]; } -- (IBAction)onPlusClick:(id)sender { - UILongPressGestureRecognizer *gesture = (UILongPressGestureRecognizer *)sender; - if (gesture.state != UIGestureRecognizerStateBegan) { - // allow only one click once time - return; - } - DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""]; - dispatch_async(dispatch_get_main_queue(), ^{ - [sheet addButtonWithTitle:NSLocalizedString(@"Save to Gallery", nil) - block:^() { - LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message); - NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; - [ChatConversationView writeMediaToGallery:name fileType:[NSString stringWithUTF8String:linphone_content_get_type(content)?:""]]; - }]; - - [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; - [sheet showInView:PhoneMainView.instance.view]; - }); -} - (IBAction)onFileClick:(id)sender { ChatConversationView *view = VIEW(ChatConversationView); @@ -545,16 +528,6 @@ } } -- (void)onResendClick:(id)event { - if (_downloadButton.hidden == NO) { - // if download button is displayed, click on it - [self onDownloadClick:event]; - } else if (_cancelButton.hidden == NO) { - [self onCancelClick:event]; - } else { - [super onResend]; - } -} - (IBAction)onImageClick:(id)event { if (_finalImage.tag == FILE_ICON_TAG) { @@ -563,7 +536,7 @@ } LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); if (state == LinphoneChatMessageStateNotDelivered) { - [self onResendClick:event]; + return; } else { if (![_messageImageView isLoading]) { ImageView *view = VIEW(ImageView); @@ -768,6 +741,14 @@ } else { _vrView.hidden = YES; } + + CGRect r = super.photoCellContentView.frame; + r.origin.y = linphone_chat_message_is_reply(super.message) ? super.replyView.view.frame.origin.y + super.replyView.view.frame.size.height + 10 : 7 ; + super.photoCellContentView.frame = r; + + r = super.photoCellContentView.frame; + r.origin.y = linphone_chat_message_is_forward(super.message) ? super.contactDateLabel.frame.origin.y + super.contactDateLabel.frame.size.height + 3 : r.origin.y; + super.photoCellContentView.frame = r; self.messageText.frame = textFrame; } @@ -841,6 +822,14 @@ static AVAudioPlayer* utilityPlayer; } } + +// menu + +-(void) onPopupMenuPressed { + [super onPopupMenuPressed]; +} + + @end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h index e90bf6b44..fe729b670 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.h +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -22,6 +22,7 @@ #import "UITextViewNoDefine.h" #import "ChatConversationTableView.h" #import "UIRoundedImageView.h" +#import "UIChatReplyBubbleView.h" #define CELL_IMAGE_X_MARGIN 100 #define IMAGE_DEFAULT_WIDTH 120 @@ -30,7 +31,7 @@ #define VOICE_RECORDING_PLAYER_WIDTH 300 -@interface UIChatBubbleTextCell : UITableViewCell +@interface UIChatBubbleTextCell : UITableViewCell @property(readonly, nonatomic) LinphoneEventLog *event; @property(readonly, nonatomic) LinphoneChatMessage *message; @@ -50,6 +51,22 @@ @property (weak, nonatomic) IBOutlet UIView *innerView; @property (weak, nonatomic) IBOutlet UILabel *ephemeralTime; @property (weak, nonatomic) IBOutlet UIImageView *ephemeralIcon; +@property ChatConversationTableView *tableController; +@property BOOL popupMenuAllowed; + +// Message popup menu +@property UITableView *popupMenu; +@property NSMutableArray *messageActionsTitles; +@property NSMutableArray *messageActionsIcons; +@property NSMutableArray *messageActionsBlocks; + +// Message reply/transfer +@property UIChatReplyBubbleView *replyView; +@property UILabel *replyOrForward; +@property (weak, nonatomic) IBOutlet UIImageView *replyTransferIcon; +@property (weak, nonatomic) IBOutlet UILabel *replyTransferLabel; +@property (weak, nonatomic) IBOutlet UIView *photoCellContentView; + @property(nonatomic) BOOL isFirst; @property(nonatomic) BOOL isLast; @@ -67,8 +84,6 @@ - (void)clearEncryptedFiles; - (void)onDelete; -- (void)onResend; -- (void)onLime; - (void)update; - (void)displayImdmStatus:(LinphoneChatMessageState)state; @@ -77,5 +92,6 @@ + (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font; + (NSString *)ContactDateForChat:(LinphoneChatMessage *)message; +(LinphoneContent *) voiceContent:(LinphoneChatMessage *)message; - +-(void) onPopupMenuPressed; +-(void) dismissPopup; @end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 8f8fed31b..ef170ae1b 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -20,6 +20,7 @@ #import "UIChatBubbleTextCell.h" #import "LinphoneManager.h" #import "PhoneMainView.h" +#import "Utils.h" #import #import @@ -28,6 +29,8 @@ #pragma mark - Lifecycle Functions + + - (id)initWithIdentifier:(NSString *)identifier { if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) { if ([identifier isEqualToString:NSStringFromClass(self.class)]) { @@ -40,22 +43,11 @@ [self addSubview:sub]; } } + + + [_innerView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onPopupMenuPressed)]]; + _messageText.userInteractionEnabled = false; - UITapGestureRecognizer *limeRecognizer = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onLime)]; - limeRecognizer.numberOfTapsRequired = 1; - //[_LIMEKO addGestureRecognizer:limeRecognizer]; - //_LIMEKO.userInteractionEnabled = YES; - UITapGestureRecognizer *resendRecognizer = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onResend)]; - resendRecognizer.numberOfTapsRequired = 1; - [_bubbleView addGestureRecognizer:resendRecognizer]; - _imdmIcon.userInteractionEnabled = YES; - UITapGestureRecognizer *resendRecognizer2 = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onResend)]; - resendRecognizer2.numberOfTapsRequired = 1; - //[_imdmLabel addGestureRecognizer:resendRecognizer2]; - //_imdmLabel.userInteractionEnabled = YES; self.contentView.userInteractionEnabled = NO; return self; @@ -205,11 +197,12 @@ _avatarImage.hidden = !_isFirst; } + // Not use [UIImage imageNamed], it takes too much time - NSString *imageName = outgoing ? @"color_A.png" : @"color_D.png"; - _backgroundColorImage.image = - [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] bundlePath],imageName]]; - + _backgroundColorImage.image = nil; + _backgroundColorImage.backgroundColor = outgoing ? [UIColor color:@"A"] : [UIColor color:@"D"]; + + // set maskedCorners if (@available(iOS 11.0, *)) { _backgroundColorImage.layer.cornerRadius = 10; @@ -255,6 +248,7 @@ frame.origin.y = _isFirst ? 20 : 0; _innerView.frame = frame; + [_messageText setAccessibilityLabel:outgoing ? @"Outgoing message" : @"Incoming message"]; if (outgoing && (state == LinphoneChatMessageStateDeliveredToUser || state == LinphoneChatMessageStateDisplayed || @@ -262,6 +256,23 @@ [self displayImdmStatus:state]; } else [self displayImdmStatus:LinphoneChatMessageStateInProgress]; + + if (linphone_chat_message_is_reply(_message)) { + if (_replyView == nil) { + _replyView = [[UIChatReplyBubbleView alloc] initWithNibName:@"UIChatReplyBubbleView" bundle:nil]; + [self.innerView addSubview:_replyView.view]; + } + _replyView.view.hidden = false; + CGRect replyFrame = CGRectMake(_contactDateLabel.frame.origin.x, _contactDateLabel.frame.origin.y+_contactDateLabel.frame.size.height,self.contactDateLabel.frame.size.width, REPLY_CHAT_BUBBLE_HEIGHT); + _replyView.view.frame = replyFrame; + [_replyView configureForMessage:linphone_chat_message_get_reply_message(_message) withDimissBlock:^{} hideDismiss:true withClickBlock:^{ + [_tableController scrollToMessage:linphone_chat_message_get_reply_message(_message)]; + }]; + } else { + if (_replyView) + _replyView.view.hidden = true; + } + } - (void)setEditing:(BOOL)editing { @@ -269,7 +280,6 @@ } - (void)setEditing:(BOOL)editing animated:(BOOL)animated { - _messageText.userInteractionEnabled = !editing; _resendRecognizer.enabled = !editing; } @@ -300,94 +310,6 @@ } } -- (void)onLime { - /*if (!_LIMEKO.hidden) - [self displayLIMEWarning];*/ -} - -- (void)onResend { - - if (!linphone_core_is_network_reachable(LC)) { - [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"send a message"] animated:YES completion:nil]; - //return; - } - - if (_message == nil || !linphone_chat_message_is_outgoing(_message)) - return; - - LinphoneChatMessageState state = linphone_chat_message_get_state(_message); - if (state != LinphoneChatMessageStateNotDelivered && state != LinphoneChatMessageStateFileTransferError) - return; - - const bctbx_list_t *contents = linphone_chat_message_get_contents(_message); - LinphoneContent *voiceContent = [UIChatBubbleTextCell voiceContent:_message]; - size_t contentCount = bctbx_list_size(contents); - if (voiceContent) - contentCount--; - - BOOL multiParts = ((linphone_chat_message_get_text_content(_message) != NULL) ? contentCount > 2 : contentCount > 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_voice_recording(content)) { - continue; - } - 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 validFilePath: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 voiceContent:voiceContent]; - }); - return; - } - if (!voiceContent && contentCount == 1 && 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]; - NSString *filePath = [LinphoneManager getMessageAppDataForKey:@"encryptedfile" inMessage:self.message]; - - [self onDelete]; - dispatch_async(dispatch_get_main_queue(), ^ { - NSData *data = NULL; - if (filePath) { - data = [NSData dataWithContentsOfFile:filePath]; - } - const char *text = linphone_chat_message_get_text_content(_message); - NSString *str = text ? [NSString stringWithUTF8String:text] : NULL; - if (localImage) { - [_chatRoomDelegate resendFile: (data?:[ChatConversationView getFileData:localImage]) withName:localImage type:@"image" key:@"localimage" message:str voiceContent:voiceContent]; - } else if (localVideo) { - [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localVideo]) withName:localVideo type:@"video" key:@"localvideo" message:str voiceContent:voiceContent]; - } else { - [_chatRoomDelegate resendFile:(data?:[ChatConversationView getFileData:localFile]) withName:localFile type:@"image" key:@"localfile" message:str voiceContent:voiceContent]; - } - }); - } else { - [self onDelete]; - double delayInSeconds = 0.4; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { - NSString *text = self.textMessage; - if (voiceContent && [text isEqualToString:@"🗻"]) - text = nil; - [_chatRoomDelegate resendChat:text withExternalUrl:nil voiceContent:voiceContent]; - }); - } -} #pragma mark - State changed handling static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState state) { LOGI(@"State for message [%p] changed to %s", msg, linphone_chat_message_state_to_string(state)); @@ -442,9 +364,15 @@ 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 REPLY_CHAT_BUBBLE_HEIGHT = 120; +static const CGFloat REPLY_OR_FORWARD_TAG_HEIGHT = 18; + + (CGSize)ViewHeightForMessage:(LinphoneChatMessage *)chat withWidth:(int)width { - return [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil]; + CGSize size = [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil]; + size.height += linphone_chat_message_is_forward(chat) || linphone_chat_message_is_reply(chat) ? REPLY_OR_FORWARD_TAG_HEIGHT : 0; + size.height += linphone_chat_message_is_reply(chat) ? REPLY_CHAT_BUBBLE_HEIGHT+5 : 0; + return size; } + (CGSize)ViewHeightForFile:(int)width { @@ -456,20 +384,38 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; } ++(NSString *)formattedDuration:(long)valueMs { + return [NSString stringWithFormat:@"%02ld:%02ld", valueMs/ 60, (valueMs % 60) ]; +} + ++(NSString *) recordingDuration:(NSString *) _voiceRecordingFile{ + NSError *error = nil; + AVAudioPlayer* utilityPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL URLWithString:_voiceRecordingFile] error:&error]; // Workaround as opening multiple linphone_players at the same time can cause crash (here for example layout refreshed whilst a voice memo is playing + return [self formattedDuration:utilityPlayer.duration]; + utilityPlayer = nil; +} + + (UIImage *)getImageFromFileName:(NSString *)fileName { NSString *extension = [[fileName.lowercaseString componentsSeparatedByString:@"."] lastObject]; UIImage *image; - if ([extension isEqualToString:@"pdf"]) - image = [UIImage imageNamed:@"file_pdf_default"]; - else if ([@[@"png", @"jpg", @"jpeg", @"bmp", @"heic"] containsObject:extension]) - image = [UIImage imageNamed:@"file_picture_default"]; - else if ([@[@"mkv", @"avi", @"mov", @"mp4"] containsObject:extension]) - image = [UIImage imageNamed:@"file_video_default"]; - else if ([@[@"wav", @"au", @"m4a"] containsObject:extension]) - image = [UIImage imageNamed:@"file_audio_default"]; - else - image = [UIImage imageNamed:@"file_default"]; - return [SwiftUtil textToImageWithDrawText:fileName inImage:image]; + NSString * text = fileName; + if ([fileName containsString:@"voice-recording"]) { + image = [UIImage imageNamed:@"file_voice_default"]; + text = [self recordingDuration:[LinphoneManager validFilePath:fileName]]; + } else { + if ([extension isEqualToString:@"pdf"]) + image = [UIImage imageNamed:@"file_pdf_default"]; + else if ([@[@"png", @"jpg", @"jpeg", @"bmp", @"heic"] containsObject:extension]) + image = [UIImage imageNamed:@"file_picture_default"]; + else if ([@[@"mkv", @"avi", @"mov", @"mp4"] containsObject:extension]) + image = [UIImage imageNamed:@"file_video_default"]; + else if ([@[@"wav", @"au", @"m4a"] containsObject:extension]) + image = [UIImage imageNamed:@"file_audio_default"]; + else + image = [UIImage imageNamed:@"file_default"]; + } + + return [SwiftUtil textToImageWithDrawText:text inImage:image]; } + (UIImage *)getImageFromContent:(LinphoneContent *)content filePath:(NSString *)filePath; { @@ -630,7 +576,8 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; size = [self computeBoundingBox:messageText size:CGSizeMake(width - CELL_MESSAGE_X_MARGIN - 4, CGFLOAT_MAX) font:messageFont]; - } else { + size.width += 4; + } else { // Just file or file with text LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(chat); NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat]; NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:chat]; @@ -671,10 +618,11 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; } else if (voiceContent){ return [self addVoicePlayerToSize:[self ViewHeightForFile:width] withMargins:true]; } else { - image = [UIChatBubbleTextCell getImageFromFileName:fileName]; + image = nil; + originalImageSize = CGSizeMake(140, 140); } - - originalImageSize = image.size; + if (image != nil) + originalImageSize = image.size; } else { if (!localImage && !localVideo) { //We are loading the image @@ -781,15 +729,45 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; int origin_x; bubbleFrame.size = [self.class ViewSizeForMessage:_message withWidth:available_width]; + if (linphone_chat_message_is_reply(_message)) { + bubbleFrame.size.width = MAX(bubbleFrame.size.width, 300); + } if (tableView.isEditing) { origin_x = 0; } else { origin_x = (is_outgoing ? self.frame.size.width - bubbleFrame.size.width : 0); } + + CGRect r = _messageText.frame; + r.origin.y = linphone_chat_message_is_reply(_message) ? _replyView.view.frame.origin.y + _replyView.view.frame.size.height + 5 : 3; + _messageText.frame = r; + + r = _messageText.frame; + r.origin.y = linphone_chat_message_is_forward(_message) ? _contactDateLabel.frame.origin.y + _contactDateLabel.frame.size.height + 5 : r.origin.y; + _messageText.frame = r; + + _replyTransferIcon.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message); + _replyTransferLabel.hidden = ! linphone_chat_message_is_reply(_message) && !linphone_chat_message_is_forward(_message); + + if (linphone_chat_message_is_reply(_message)) { + CGRect replyFrame = CGRectMake(10, _replyTransferLabel.frame.origin.y+_replyTransferLabel.frame.size.height+5,MAX(self.contactDateLabel.frame.size.width-20,180), REPLY_CHAT_BUBBLE_HEIGHT); + _replyView.view.frame = replyFrame; + _replyTransferIcon.image = [UIImage imageNamed:@"menu_reply_default"]; + _replyTransferLabel.text = NSLocalizedString(@"Answer",nil); + _replyTransferLabel.textColor = [UIColor lightGrayColor]; + } + + if (linphone_chat_message_is_forward(_message)) { + _replyTransferIcon.image = [UIImage imageNamed:@"menu_forward_default"]; + _replyTransferLabel.text = NSLocalizedString(@"Transferred",nil); + _replyTransferLabel.textColor = [UIColor darkGrayColor]; + } bubbleFrame.origin.x = origin_x; _bubbleView.frame = bubbleFrame; + + } } @@ -817,4 +795,173 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 44; return mediaSize; } + +// Message popup menu +// Copy text -> if has text +// Transfer -> always +// Reply -> always +// IMDM Status -> out +// Delete -> always + + +-(void) buildActions { + LinphoneChatMessage *message = self.message; + _messageActionsTitles = [[NSMutableArray alloc] init]; + _messageActionsBlocks = [[NSMutableArray alloc] init]; + _messageActionsIcons = [[NSMutableArray alloc] init]; + + UIChatBubbleTextCell *thiz = self; + + LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); + if (state == LinphoneChatMessageStateNotDelivered || state == LinphoneChatMessageStateFileTransferError) { + [_messageActionsTitles addObject:NSLocalizedString(@"Resend", nil)]; + [_messageActionsIcons addObject:@"menu_resend_default"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + if (!linphone_core_is_network_reachable(LC)) { + [PhoneMainView.instance presentViewController:[LinphoneUtils networkErrorView:@"send a message"] animated:YES completion:nil]; + return; + } + linphone_chat_message_send(message); + }]; + } + + + if (linphone_chat_message_get_utf8_text(message)) { + [_messageActionsTitles addObject:NSLocalizedString(@"Copy text", nil)]; + [_messageActionsIcons addObject:@"menu_copy_text_default"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + [UIPasteboard.generalPasteboard setString:[NSString stringWithUTF8String:linphone_chat_message_get_text_content(message)]]; + }]; + } + + + [_messageActionsTitles addObject:NSLocalizedString(@"Forward", nil)]; + [_messageActionsIcons addObject:@"menu_forward_default"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + VIEW(ChatConversationView).pendingForwardMessage = message; + [PhoneMainView.instance changeCurrentView:VIEW(ChatsListView).compositeViewDescription]; + }]; + + + + [_messageActionsTitles addObject:NSLocalizedString(@"Reply", nil)]; + [_messageActionsIcons addObject:@"menu_reply_default"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + [VIEW(ChatConversationView) initiateReplyViewForMessage:message]; + }]; + + if (linphone_chat_message_is_outgoing(self.message) && linphone_chat_room_get_nb_participants(linphone_chat_message_get_chat_room(self.message)) > 1) { + [_messageActionsTitles addObject:NSLocalizedString(@"Infos", nil)]; + [_messageActionsIcons addObject:@"menu_info"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + ChatConversationImdnView *view = VIEW(ChatConversationImdnView); + view.msg = message; + [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; + }]; + } + + [_messageActionsTitles addObject:NSLocalizedString(@"Delete", nil)]; + [_messageActionsIcons addObject:@"menu_delete"]; + [_messageActionsBlocks addObject:^{ + [thiz dismissPopup]; + linphone_chat_room_delete_message(linphone_chat_message_get_chat_room(message), message); + [VIEW(ChatConversationView).tableController reloadData]; + }]; +} + +-(void) onPopupMenuPressed { + if (_popupMenu != nil) + [self dismissPopup]; + + if (!self.popupMenuAllowed) + return; + + + [VIEW(ChatConversationView).tableController dismissMessagesPopups]; + self.innerView.layer.borderWidth = 3; + self.innerView.layer.borderColor = [UIColor color:@"A"].CGColor; + [self buildActions]; + int width = 250; + int cellHeight = 44; + int numberOfItems = (int) _messageActionsTitles.count; + CGRect screenRect = UIScreen.mainScreen.bounds; + int menuHeight = numberOfItems * cellHeight; + + CGRect frame = CGRectMake( + linphone_chat_message_is_outgoing(self.message) ? screenRect.size.width - width - 10 : 10, + (self.frame.origin.y + self.frame.size.height) - [VIEW(ChatConversationView).tableController .tableView contentOffset].y > screenRect.size.height /2 ? self.frame.origin.y - menuHeight - 10: self.frame.origin.y + self.frame.size.height, + width, + menuHeight); + + _popupMenu = [[UITableView alloc]initWithFrame:frame]; + _popupMenu.dataSource = self; + _popupMenu.delegate = self; + _popupMenu.layer.shadowColor = [UIColor lightGrayColor].CGColor; + _popupMenu.layer.shadowOpacity = 0.5; + _popupMenu.layer.shadowOffset = CGSizeZero; + _popupMenu.layer.shadowRadius = 5; + _popupMenu.layer.masksToBounds = false; + _popupMenu.tableFooterView = [UIView new]; + _popupMenu.editing = NO; + _popupMenu.userInteractionEnabled = true; + [_popupMenu reloadData]; + [VIEW(ChatConversationView).tableController.view addSubview:_popupMenu]; + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapOutsideMenu:)]; + tapGestureRecognizer.cancelsTouchesInView = NO; + tapGestureRecognizer.numberOfTapsRequired = 1; + [VIEW(ChatConversationView).tableController.view addGestureRecognizer:tapGestureRecognizer]; +} + +-(void) dismissPopup { + if (!_popupMenu) + return; + [_popupMenu removeFromSuperview]; + _popupMenu = nil; + self.innerView.layer.borderWidth = 0; + [self setNeedsLayout]; +} + + +-(void) tapOutsideMenu:(UITapGestureRecognizer *) g { + CGPoint p = [g locationInView:VIEW(ChatConversationView).tableController.view]; + if (!CGRectContainsPoint(_popupMenu.frame,p)) { + [self dismissPopup]; + } +} + +-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + void (^ myblock)(void) = [_messageActionsBlocks objectAtIndex:indexPath.row]; + [self dismissPopup]; + myblock(); +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return [_messageActionsTitles count]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = [[UITableViewCell alloc] init]; + cell.imageView.image = [[UIImage imageNamed:[_messageActionsIcons objectAtIndex:indexPath.row]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + cell.textLabel.text = [_messageActionsTitles objectAtIndex:indexPath.row]; + cell.imageView.contentMode = UIViewContentModeScaleAspectFit; + if ([[_messageActionsIcons objectAtIndex:indexPath.row] isEqualToString:@"menu_delete"]) { + cell.textLabel.textColor = UIColor.redColor; + cell.imageView.tintColor = UIColor.redColor; + } else { + cell.imageView.tintColor = PhoneMainView.instance.darkMode ? UIColor.whiteColor : UIColor.blackColor; + } + return cell; +} + + + @end diff --git a/Classes/LinphoneUI/UIChatCell.h b/Classes/LinphoneUI/UIChatCell.h index d5d0327b9..29343ea40 100644 --- a/Classes/LinphoneUI/UIChatCell.h +++ b/Classes/LinphoneUI/UIChatCell.h @@ -38,6 +38,8 @@ @property(weak, nonatomic) IBOutlet UILabel *unreadCountLabel; @property (weak, nonatomic) IBOutlet UIImageView *imdmIcon; @property (weak, nonatomic) IBOutlet UIImageView *ephemeral; +@property (weak, nonatomic) IBOutlet UIImageView *forwardIcon; + - (id)initWithIdentifier:(NSString*)identifier; diff --git a/Classes/LinphoneUI/UIChatCell.m b/Classes/LinphoneUI/UIChatCell.m index 299898dbf..3fc01de59 100644 --- a/Classes/LinphoneUI/UIChatCell.m +++ b/Classes/LinphoneUI/UIChatCell.m @@ -46,6 +46,7 @@ - (void)setChatRoom:(LinphoneChatRoom *)achat { chatRoom = achat; [self update]; + [self.forwardIcon setImageNamed:@"forward_message_default" tintColor:PhoneMainView.instance.darkMode ? UIColor.whiteColor : UIColor.darkGrayColor]; } #pragma mark - diff --git a/Classes/LinphoneUI/UIChatReplyBubbleView.h b/Classes/LinphoneUI/UIChatReplyBubbleView.h new file mode 100644 index 000000000..b9c175ae2 --- /dev/null +++ b/Classes/LinphoneUI/UIChatReplyBubbleView.h @@ -0,0 +1,40 @@ +/* + * 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface UIChatReplyBubbleView : UIViewController +@property (weak, nonatomic) IBOutlet UILabel *senderName; +@property (weak, nonatomic) IBOutlet UIButton *dismissButton; +@property (weak, nonatomic) IBOutlet UIView *leftBar; +@property (weak, nonatomic) IBOutlet UIView *rightBar; +@property LinphoneChatMessage *message; +@property (weak, nonatomic) IBOutlet UILabel *textContent; +@property void (^ dismissAction)(void); +@property void (^ clickAction)(void); +@property (weak, nonatomic) IBOutlet UICollectionView *contentCollection; +@property NSArray *dataContent; +@property (weak, nonatomic) IBOutlet UILabel *originalMessageGone; + +-(void) configureForMessage:(LinphoneChatMessage *)message withDimissBlock:(void (^)(void))dismissBlock hideDismiss:(BOOL)hideDismiss withClickBlock:(void (^)(void))clickBlock; +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/LinphoneUI/UIChatReplyBubbleView.m b/Classes/LinphoneUI/UIChatReplyBubbleView.m new file mode 100644 index 000000000..e6e523561 --- /dev/null +++ b/Classes/LinphoneUI/UIChatReplyBubbleView.m @@ -0,0 +1,159 @@ +/* + * 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 "UIChatReplyBubbleView.h" +#import "linphoneapp-Swift.h" +#import "Utils.h" + +@interface UIChatReplyBubbleView () + +@end + +@implementation UIChatReplyBubbleView + + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; +} + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + return self; +} + +-(void) viewDidLoad { + _contentCollection.dataSource = self; + [_contentCollection registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:@"dataContent"]; +} + + +-(void) configureForMessage:(LinphoneChatMessage *)message withDimissBlock:(void (^)(void))dismissBlock hideDismiss:(BOOL)hideDismiss withClickBlock:(void (^)(void))clickBlock{ + if (!message) { + _textContent.hidden = true; + _dismissButton.hidden = true; + _contentCollection.hidden = true; + _senderName.hidden = true; + _originalMessageGone.hidden = false; + return; + } + if (hideDismiss) { + self.view.layer.cornerRadius = 10; + self.view.layer.masksToBounds = true; + } + _originalMessageGone.hidden = true; + self.message = message; + self.dataContent = [self loadDataContent]; + NSString *sender = [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(message)]; + _senderName.text = sender; + const char * text = linphone_chat_message_get_text_content(message); + if (text && strlen(text) == 0) + text = nil; + _textContent.text = text ? [NSString stringWithUTF8String:text] : @""; + _dismissButton.hidden = hideDismiss; + _dismissAction = dismissBlock; + _clickAction = clickBlock; + if (hideDismiss) { + UITapGestureRecognizer *singleFingerTap = + [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(onClick)]; + [self.view addGestureRecognizer:singleFingerTap]; + } + else + [_dismissButton addTarget:self action:@selector(dismissClick) forControlEvents:UIControlEventTouchUpInside]; + + + self.view.backgroundColor = hideDismiss ? UIColor.whiteColor :(linphone_chat_message_is_outgoing(message) ? [[UIColor color:@"A"] colorWithAlphaComponent:0.2] : [[UIColor color:@"D"] colorWithAlphaComponent:0.2]); + _leftBar.backgroundColor = linphone_chat_message_is_outgoing(message) ? [UIColor color:@"A"] : [UIColor color:@"D"]; + _leftBar.hidden = !hideDismiss; + _rightBar.backgroundColor = self.view.backgroundColor; + + + // Resize frame -> text or content only = 100, 145 otherwise + _contentCollection.hidden = self.dataContent.count == 0; + + CGRect r = self.view.frame ; + r.size.width = self.view.superview.frame.size.width; + self.view.frame = r; + + if (self.dataContent.count == 0) { + CGRect r = _textContent.frame; + r.origin.y = _contentCollection.frame.origin.y; + r.size.height = 87; + _textContent.frame = r; + } + + if (text == nil) { + CGRect r = _contentCollection.frame; + r.origin.y = 30; + _contentCollection.frame = r; + } +} + + +-(NSArray *) loadDataContent { + NSMutableArray *result = [[NSMutableArray alloc] init]; + const bctbx_list_t *contents = linphone_chat_message_get_contents(_message); + const char * text = linphone_chat_message_get_utf8_text(_message); + if (text && bctbx_list_size(contents) == 1) + return result; + + for (const bctbx_list_t * it = contents; it != NULL; it=bctbx_list_next(it)){ + LinphoneContent *content = (LinphoneContent *)it->data; + if (linphone_content_is_text(content)) + continue; + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + NSMutableDictionary *encrptedFilePaths = encrptedFilePaths = [LinphoneManager getMessageAppDataForKey:@"encryptedfiles" inMessage:_message]; + NSString *filePath = encrptedFilePaths ? [encrptedFilePaths valueForKey:name] : nil; + if (filePath == NULL) { + filePath = [LinphoneManager validFilePath:name]; + } + [result addObject:[UIChatBubbleTextCell getImageFromContent:content filePath:filePath]]; + } + return result; +} + +-(void) dismissClick { + _dismissAction(); +} + +-(void) onClick { + _clickAction(); +} + +-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return self.dataContent.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"dataContent" forIndexPath:indexPath]; + UIImageView *img = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)]; + img.image = [self.dataContent objectAtIndex:indexPath.row]; + [cell.contentView addSubview:img]; + return cell; +} + + + + +@end diff --git a/Classes/LinphoneUI/UIConfirmationDialog.h b/Classes/LinphoneUI/UIConfirmationDialog.h index ad0a0b5b0..c2d9feadc 100644 --- a/Classes/LinphoneUI/UIConfirmationDialog.h +++ b/Classes/LinphoneUI/UIConfirmationDialog.h @@ -45,6 +45,7 @@ typedef void (^UIConfirmationBlock)(void); @property(weak, nonatomic) IBOutlet UIRoundBorderedButton *cancelButton; @property (weak, nonatomic) IBOutlet UIImageView *securityImage; +@property (weak, nonatomic) IBOutlet UIImageView *forwardImage; @property(weak, nonatomic) IBOutlet UIRoundBorderedButton *confirmationButton; @property (weak, nonatomic) IBOutlet UIView *authView; @property(weak, nonatomic) IBOutlet UILabel *titleLabel; diff --git a/Classes/LinphoneUI/en.lproj/UIChatReplyBubbleView.strings b/Classes/LinphoneUI/en.lproj/UIChatReplyBubbleView.strings new file mode 100644 index 000000000..cfa28ba85 --- /dev/null +++ b/Classes/LinphoneUI/en.lproj/UIChatReplyBubbleView.strings @@ -0,0 +1,9 @@ + +/* Class = "UILabel"; text = "Original message removed"; ObjectID = "B26-sw-o4w"; */ +"B26-sw-o4w.text" = "Original message does not exist anymore."; + +/* Class = "UILabel"; text = "Label"; ObjectID = "Czc-VH-qiH"; */ +"Czc-VH-qiH.text" = "Label"; + +/* Class = "UILabel"; text = "Label"; ObjectID = "uuW-tW-1Sj"; */ +"uuW-tW-1Sj.text" = "Label"; diff --git a/Classes/LinphoneUI/fr.lproj/UIChatReplyBubbleView.strings b/Classes/LinphoneUI/fr.lproj/UIChatReplyBubbleView.strings new file mode 100644 index 000000000..de5a5c443 --- /dev/null +++ b/Classes/LinphoneUI/fr.lproj/UIChatReplyBubbleView.strings @@ -0,0 +1,9 @@ + +/* Class = "UILabel"; text = "Original message removed"; ObjectID = "B26-sw-o4w"; */ +"B26-sw-o4w.text" = "Le message original n'existe plus."; + +/* Class = "UILabel"; text = "Label"; ObjectID = "Czc-VH-qiH"; */ +"Czc-VH-qiH.text" = "Label"; + +/* Class = "UILabel"; text = "Label"; ObjectID = "uuW-tW-1Sj"; */ +"uuW-tW-1Sj.text" = "Label"; diff --git a/Classes/PhoneMainView.h b/Classes/PhoneMainView.h index 60633724e..adfe87b5a 100644 --- a/Classes/PhoneMainView.h +++ b/Classes/PhoneMainView.h @@ -121,6 +121,7 @@ - (BOOL)isIphoneXDevice; + (int)iphoneStatusBarHeight; +-(BOOL) darkMode; @end diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m index 680712541..793243495 100644 --- a/Classes/PhoneMainView.m +++ b/Classes/PhoneMainView.m @@ -957,4 +957,15 @@ void main_view_chat_room_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomSta [controller dismissModalViewControllerAnimated:YES]; } +#pragma mark - Light/Dark mode + +-(BOOL) darkMode { + if (@available(iOS 13.0, *)) { + UITraitCollection *collection = [UITraitCollection currentTraitCollection]; + return collection.userInterfaceStyle == UIUserInterfaceStyleDark; + } else { + return false; + } +} + @end diff --git a/Classes/SwiftUtil.swift b/Classes/SwiftUtil.swift index ff1701b87..ff9278263 100644 --- a/Classes/SwiftUtil.swift +++ b/Classes/SwiftUtil.swift @@ -1,46 +1,69 @@ -// -// SwiftUtil.swift -// linphone -// -// Created by Tof on 23/07/2021. -// +/* +* 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 UIKit @objc class SwiftUtil: NSObject { - + @objc static func textToImage(drawText text: String, inImage image: UIImage) -> UIImage { let textColor = UIColor.black - let textFont = UIFont(name: "Helvetica", size: 12)! + let fontMax = UIFont.systemFont(ofSize: 30) let backgroundColor = UIColor.white let size = CGSize(width: 120, height: 120) - + let scale = UIScreen.main.scale UIGraphicsBeginImageContextWithOptions(size, false, scale) let context = UIGraphicsGetCurrentContext() backgroundColor.setFill() context!.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) - - let paragraph = NSMutableParagraphStyle() - paragraph.alignment = .center - let textFontAttributes = [ - NSAttributedString.Key.font: textFont, - NSAttributedString.Key.foregroundColor: textColor, - NSAttributedString.Key.paragraphStyle: paragraph, - ] as [NSAttributedString.Key : Any] + image.draw(in: CGRect(origin: CGPoint(x: size.width/2 - (image.size.width)/2,y: 5), size: image.size)) + + let label = UILabel(frame: CGRect(x: 0,y: 0,width: size.width,height: 50)) + label.numberOfLines = 0 + label.font = fontMax + label.adjustsFontSizeToFitWidth = true + label.text = text + label.textColor = textColor + label.textAlignment = .center + label.allowsDefaultTighteningForTruncation = true + label.lineBreakMode = .byTruncatingTail + imageWithLabel(label: label).draw(in: CGRect(origin: CGPoint(x:0,y: 60), size: CGSize(width: size.width,height: 50))) + let view = UIView(frame: CGRect(x: 0,y: 0,width: size.width,height: 50)) + view.addSubview(label) + label.sizeToFit() + - image.draw(in: CGRect(origin: CGPoint(x: size.width/2 - (image.size.width)/2,y: 15), size: image.size)) - - let rect = CGRect(origin: CGPoint(x: 0,y: 70), size: CGSize(width: size.width, height: 30)) - text.draw(in: rect, withAttributes: textFontAttributes) - let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() - + return newImage! } + static func imageWithLabel(label: UILabel) -> UIImage { + UIGraphicsBeginImageContextWithOptions(label.frame.size, false, 0.0) + label.layer.render(in: UIGraphicsGetCurrentContext()!) + let img = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return img + } + } diff --git a/Classes/Utils/FileTransferDelegate.h b/Classes/Utils/FileTransferDelegate.h index 21ff1d605..3fa22bb55 100644 --- a/Classes/Utils/FileTransferDelegate.h +++ b/Classes/Utils/FileTransferDelegate.h @@ -24,11 +24,10 @@ @interface FileTransferDelegate : NSObject -- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom andVoiceContent:(LinphoneContent *)voiceContent; -- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key voiceContent:(LinphoneContent *)voiceContent; +- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom rootMessage:(LinphoneChatMessage *)rootMessage; +- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key rootMessage:(LinphoneChatMessage *)rootMessage; - (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality; -- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name; -- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom; +- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name rootMessage:(LinphoneChatMessage *)rootMessage; - (void)cancel; - (BOOL)download:(LinphoneChatMessage *)message; - (void)stopAndDestroy; diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 9ad65457e..0738c54b4 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -102,7 +102,7 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, } } -- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key voiceContent:(LinphoneContent *)voiceContent{ +- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key rootMessage:(LinphoneChatMessage *)rootMessage{ if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) { LOGW(@"fileTransferDelegates has already added %p", self); return; @@ -115,7 +115,8 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, linphone_content_set_subtype(content, [subtype UTF8String]); linphone_content_set_name(content, [name UTF8String]); linphone_content_set_file_path(content, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:name].UTF8String); - _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); + _message = rootMessage; + linphone_chat_message_add_file_content(_message, content); BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) linphone_chat_message_add_text_content(_message, [_text UTF8String]); @@ -125,17 +126,16 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message]; - if (voiceContent) - linphone_chat_message_add_content(_message, voiceContent); + LOGI(@"%p Uploading content from message %p", self, _message); linphone_chat_message_send(_message); } -- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom andVoiceContent:(LinphoneContent *)voiceContent{ +- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom rootMessage:(LinphoneChatMessage *)rootMessage{ [LinphoneManager.instance.fileTransferDelegates addObject:self]; - _message = linphone_chat_room_create_empty_message(chatRoom); + _message = rootMessage; NSMutableArray *names = [[NSMutableArray alloc] init]; NSMutableArray *types = [[NSMutableArray alloc] init]; @@ -165,8 +165,7 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message, // todo indication progress [LinphoneManager setValueInMessageAppData:names forKey:@"multiparts" inMessage:_message]; [LinphoneManager setValueInMessageAppData:types forKey:@"multipartstypes" inMessage:_message]; - if (voiceContent) - linphone_chat_message_add_content(_message, voiceContent); + LOGI(@"%p Uploading content from message %p", self, _message); linphone_chat_message_send(_message); } @@ -175,21 +174,17 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *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); - [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage" voiceContent:nil]; + [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage" rootMessage:nil]; } -- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom { - NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; - [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo" voiceContent:nil]; -} -- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name { +- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name rootMessage:(LinphoneChatMessage *)rootMessage { NSURL *url = [ChatConversationView getFileUrl:name]; AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file"; NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; - [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key voiceContent:nil]; + [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key rootMessage:rootMessage]; } - (BOOL)download:(LinphoneChatMessage *)message { diff --git a/Classes/Utils/Utils.h b/Classes/Utils/Utils.h index d515926af..207521dc9 100644 --- a/Classes/Utils/Utils.h +++ b/Classes/Utils/Utils.h @@ -74,6 +74,13 @@ typedef enum { @end +@interface UIImageView (ImageWithTint) + +- (void)setImageNamed:(NSString *)name tintColor:(UIColor *)color; +- (void)setImageNamed:(NSString *)name tintColorLetter:(NSString *)letter; + +@end + @interface NSString (linphoneExt) - (NSString *)md5; @@ -111,6 +118,8 @@ typedef enum { - (UIColor *)darkerColor; ++(UIColor *)color:(NSString *)letter; + @end @interface UIImage (ForceDecode) diff --git a/Classes/Utils/Utils.m b/Classes/Utils/Utils.m index 7ae1fedca..b9cbfaaa1 100644 --- a/Classes/Utils/Utils.m +++ b/Classes/Utils/Utils.m @@ -575,6 +575,23 @@ @end + + +@implementation UIImageView (ImageWithTint) + +- (void)setImageNamed:(NSString *)name tintColor:(UIColor *)color { + self.image = [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + self.tintColor = color; +} + +- (void)setImageNamed:(NSString *)name tintColorLetter:(NSString *)letter { + UIColor *color = [UIColor color:letter]; + self.image = [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + self.tintColor = color; +} + +@end + @implementation NSString (md5) - (NSString *)md5 { @@ -804,6 +821,19 @@ return [self lumColor:0.75]; } + +static NSMutableDictionary *letterColors = nil; + ++(UIColor *)color:(NSString *)letter { + if (letterColors == nil) + letterColors = [[NSMutableDictionary alloc] init]; + if (![letterColors objectForKey:letter]) { + UIImage *colorImage = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/color_%@.png",[[NSBundle mainBundle] bundlePath],letter]]; + [letterColors setObject:[UIColor colorWithPatternImage:colorImage] forKey:letter]; + } + return [letterColors objectForKey:letter]; +} + @end @implementation UIImage (ForceDecode) diff --git a/Podfile b/Podfile index d5ec02b57..7c061bc30 100644 --- a/Podfile +++ b/Podfile @@ -5,7 +5,7 @@ source "https://github.com/CocoaPods/Specs.git" def all_pods if ENV['PODFILE_PATH'].nil? - pod 'linphone-sdk', '~> 5.1.0-alpha.11+950fc62' + pod 'linphone-sdk', '5.1.0-alpha.50+724b895' else pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk end diff --git a/Resources/fr.lproj/Localizable.strings b/Resources/fr.lproj/Localizable.strings index 318063fa6..42321344b 100644 Binary files a/Resources/fr.lproj/Localizable.strings and b/Resources/fr.lproj/Localizable.strings differ diff --git a/Resources/images/cancel_forward.png b/Resources/images/cancel_forward.png new file mode 100644 index 000000000..8043430fe Binary files /dev/null and b/Resources/images/cancel_forward.png differ diff --git a/Resources/images/file_voice_default.png b/Resources/images/file_voice_default.png new file mode 100644 index 000000000..a492d31a5 Binary files /dev/null and b/Resources/images/file_voice_default.png differ diff --git a/Resources/images/forward_message_default.png b/Resources/images/forward_message_default.png new file mode 100644 index 000000000..b51542ec8 Binary files /dev/null and b/Resources/images/forward_message_default.png differ diff --git a/Resources/images/menu_copy_text_default.png b/Resources/images/menu_copy_text_default.png new file mode 100644 index 000000000..ccdef4e4b Binary files /dev/null and b/Resources/images/menu_copy_text_default.png differ diff --git a/Resources/images/menu_delete.png b/Resources/images/menu_delete.png new file mode 100644 index 000000000..947c0abf4 Binary files /dev/null and b/Resources/images/menu_delete.png differ diff --git a/Resources/images/menu_forward_default.png b/Resources/images/menu_forward_default.png new file mode 100644 index 000000000..910450bf9 Binary files /dev/null and b/Resources/images/menu_forward_default.png differ diff --git a/Resources/images/menu_info.png b/Resources/images/menu_info.png new file mode 100644 index 000000000..d45da33d5 Binary files /dev/null and b/Resources/images/menu_info.png differ diff --git a/Resources/images/menu_reply_default.png b/Resources/images/menu_reply_default.png new file mode 100644 index 000000000..8aa5b5e1e Binary files /dev/null and b/Resources/images/menu_reply_default.png differ diff --git a/Resources/images/menu_resend_default.png b/Resources/images/menu_resend_default.png new file mode 100644 index 000000000..5f5ab4ddd Binary files /dev/null and b/Resources/images/menu_resend_default.png differ diff --git a/Resources/images/reply_cancel.png b/Resources/images/reply_cancel.png new file mode 100644 index 000000000..8156bd629 Binary files /dev/null and b/Resources/images/reply_cancel.png differ diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index b02841227..e02e00fe5 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -686,6 +686,18 @@ C64A85522667B74100252AD2 /* ephemeral_messages_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C64A85512667B74100252AD2 /* ephemeral_messages_default.png */; }; C666756F264C925800A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; C6667571264C925B00A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; + C66B03BB26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */; }; + C66B040A26EFDA55009B5EDC /* reply_cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = C66B040926EFDA54009B5EDC /* reply_cancel.png */; }; + C66B040E26F095D1009B5EDC /* cancel_forward.png in Resources */ = {isa = PBXBuildFile; fileRef = C66B040D26F095CE009B5EDC /* cancel_forward.png */; }; + C6A1BB3526E8815400540D50 /* menu_info.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3126E8815300540D50 /* menu_info.png */; }; + C6A1BB3626E8815400540D50 /* menu_forward_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3226E8815400540D50 /* menu_forward_default.png */; }; + C6A1BB3726E8815400540D50 /* menu_copy_text_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3326E8815400540D50 /* menu_copy_text_default.png */; }; + C6A1BB3826E8815400540D50 /* menu_reply_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3426E8815400540D50 /* menu_reply_default.png */; }; + C6A1BB3A26E881E100540D50 /* menu_delete.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3926E881E100540D50 /* menu_delete.png */; }; + C6A1BB3E26E882D000540D50 /* UIChatReplyBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = C6A1BB3C26E882D000540D50 /* UIChatReplyBubbleView.m */; }; + C6A1BB4126E889AD00540D50 /* forward_message_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4026E889AD00540D50 /* forward_message_default.png */; }; + C6A1BB4326E88F7C00540D50 /* menu_resend_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4226E88F7C00540D50 /* menu_resend_default.png */; }; + C6A1BB4526E890BD00540D50 /* file_voice_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4426E890BD00540D50 /* file_voice_default.png */; }; C6B4444226AAD0980076C517 /* file_video_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443D26AAD0970076C517 /* file_video_default.png */; }; C6B4444326AAD0980076C517 /* file_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443E26AAD0970076C517 /* file_default.png */; }; C6B4444426AAD0980076C517 /* file_picture_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443F26AAD0970076C517 /* file_picture_default.png */; }; @@ -1736,6 +1748,21 @@ C64A854D2667B67200252AD2 /* EphemeralSettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EphemeralSettingsView.m; sourceTree = ""; }; C64A854F2667B67A00252AD2 /* EphemeralSettingsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EphemeralSettingsView.xib; sourceTree = ""; }; C64A85512667B74100252AD2 /* ephemeral_messages_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ephemeral_messages_default.png; sourceTree = ""; }; + C66B03BC26E8EB1A009B5EDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatReplyBubbleView.xib; sourceTree = ""; }; + C66B03C126E8EB82009B5EDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; }; + C66B03C326E8EB87009B5EDC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; }; + C66B040926EFDA54009B5EDC /* reply_cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = reply_cancel.png; sourceTree = ""; }; + C66B040D26F095CE009B5EDC /* cancel_forward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cancel_forward.png; sourceTree = ""; }; + C6A1BB3126E8815300540D50 /* menu_info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_info.png; sourceTree = ""; }; + C6A1BB3226E8815400540D50 /* menu_forward_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_forward_default.png; sourceTree = ""; }; + C6A1BB3326E8815400540D50 /* menu_copy_text_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_copy_text_default.png; sourceTree = ""; }; + C6A1BB3426E8815400540D50 /* menu_reply_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_reply_default.png; sourceTree = ""; }; + C6A1BB3926E881E100540D50 /* menu_delete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_delete.png; sourceTree = ""; }; + C6A1BB3B26E882D000540D50 /* UIChatReplyBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatReplyBubbleView.h; sourceTree = ""; }; + C6A1BB3C26E882D000540D50 /* UIChatReplyBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatReplyBubbleView.m; sourceTree = ""; }; + C6A1BB4026E889AD00540D50 /* forward_message_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = forward_message_default.png; sourceTree = ""; }; + C6A1BB4226E88F7C00540D50 /* menu_resend_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_resend_default.png; sourceTree = ""; }; + C6A1BB4426E890BD00540D50 /* file_voice_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_voice_default.png; sourceTree = ""; }; C6B4443D26AAD0970076C517 /* file_video_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_video_default.png; sourceTree = ""; }; C6B4443E26AAD0970076C517 /* file_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_default.png; sourceTree = ""; }; C6B4443F26AAD0970076C517 /* file_picture_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_picture_default.png; sourceTree = ""; }; @@ -2190,6 +2217,9 @@ 2214EB7012F84668002A5394 /* LinphoneUI */ = { isa = PBXGroup; children = ( + C6A1BB3B26E882D000540D50 /* UIChatReplyBubbleView.h */, + C6A1BB3C26E882D000540D50 /* UIChatReplyBubbleView.m */, + C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */, 63F1DF421BCE618E00EDED90 /* UIAddressTextField.h */, 63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */, 63C441C11BBC23ED0053DC5E /* UIAssistantTextField.h */, @@ -2471,6 +2501,16 @@ 633FEBE11D3CD5570014B822 /* images */ = { isa = PBXGroup; children = ( + C66B040D26F095CE009B5EDC /* cancel_forward.png */, + C66B040926EFDA54009B5EDC /* reply_cancel.png */, + C6A1BB4426E890BD00540D50 /* file_voice_default.png */, + C6A1BB4226E88F7C00540D50 /* menu_resend_default.png */, + C6A1BB4026E889AD00540D50 /* forward_message_default.png */, + C6A1BB3926E881E100540D50 /* menu_delete.png */, + C6A1BB3326E8815400540D50 /* menu_copy_text_default.png */, + C6A1BB3226E8815400540D50 /* menu_forward_default.png */, + C6A1BB3126E8815300540D50 /* menu_info.png */, + C6A1BB3426E8815400540D50 /* menu_reply_default.png */, C6B4444026AAD0970076C517 /* file_audio_default.png */, C6B4443E26AAD0970076C517 /* file_default.png */, C6B4444126AAD0970076C517 /* file_pdf_default.png */, @@ -3414,6 +3454,7 @@ 633FEF3F1D3CD55A0014B822 /* security_pending@2x.png in Resources */, 24BFAAA4209B0630004F47A7 /* linphone_logo.png in Resources */, 633FEDC41D3CD5590014B822 /* call_hangup_disabled.png in Resources */, + C66B03BB26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib in Resources */, 633FEDA81D3CD5590014B822 /* backspace_default.png in Resources */, 636316D11A1DEBCB0009B839 /* AboutView.xib in Resources */, 8CBD7BA620B6B82400E5DCC0 /* UIChatConversationInfoTableViewCell.xib in Resources */, @@ -3480,6 +3521,7 @@ 633FEF331D3CD55A0014B822 /* route_speaker_selected@2x.png in Resources */, 633FEE6C1D3CD5590014B822 /* footer_dialer_disabled.png in Resources */, 633FEF231D3CD55A0014B822 /* route_bluetooth_default@2x.png in Resources */, + C6A1BB4526E890BD00540D50 /* file_voice_default.png in Resources */, 633FED9C1D3CD5590014B822 /* add_field_default.png in Resources */, 633FEE411D3CD5590014B822 /* contacts_all_selected@2x.png in Resources */, D38187F815FE355D00C3EDCA /* TabBarView.xib in Resources */, @@ -3554,6 +3596,7 @@ 633FEEED1D3CD55A0014B822 /* numpad_hash_over.png in Resources */, 633FEE1F1D3CD5590014B822 /* chat_start_body_disabled@2x.png in Resources */, 633FEEF81D3CD55A0014B822 /* numpad_star_over~ipad.png in Resources */, + C6A1BB3826E8815400540D50 /* menu_reply_default.png in Resources */, 633FEF301D3CD55A0014B822 /* route_speaker_disabled.png in Resources */, 639CEAFD1A1DF4D9004DE38F /* StatusBarView.xib in Resources */, 633FEDE91D3CD5590014B822 /* call_status_missed~ipad@2x.png in Resources */, @@ -3587,6 +3630,7 @@ 633FEEC81D3CD55A0014B822 /* numpad_5_over~ipad@2x.png in Resources */, 61586B91217A175D0038AC45 /* menu_recordings.png in Resources */, 633FEF1B1D3CD55A0014B822 /* presence_away@2x.png in Resources */, + C6A1BB3A26E881E100540D50 /* menu_delete.png in Resources */, 633FEE281D3CD5590014B822 /* checkbox_unchecked.png in Resources */, 633FEE9D1D3CD55A0014B822 /* numpad_0_over.png in Resources */, 633FEEC21D3CD55A0014B822 /* numpad_4~ipad@2x.png in Resources */, @@ -3603,6 +3647,7 @@ 633FEEC01D3CD55A0014B822 /* numpad_4_over~ipad@2x.png in Resources */, 61586B8B217A17320038AC45 /* menu_link_account@2x.png in Resources */, 63CDC4661C3BDE370085F529 /* shortring.caf in Resources */, + C6A1BB4126E889AD00540D50 /* forward_message_default.png in Resources */, 633FEDD51D3CD5590014B822 /* call_quality_indicator_4@2x.png in Resources */, 633FEDE71D3CD5590014B822 /* call_status_missed@2x.png in Resources */, 615A2821217F6FBF0060F920 /* security_alert_indicator@2x.png in Resources */, @@ -3733,6 +3778,7 @@ 24BFAAA5209B0630004F47A7 /* contacts_sip_default.png in Resources */, 633FEF441D3CD55A0014B822 /* speaker_default.png in Resources */, 639CEB031A1DF4EB004DE38F /* UICompositeView.xib in Resources */, + C6A1BB4326E88F7C00540D50 /* menu_resend_default.png in Resources */, 633FEF3A1D3CD55A0014B822 /* security_ko.png in Resources */, 615A283A2180788E0060F920 /* security_toogle_button.png in Resources */, 633FEDA01D3CD5590014B822 /* avatar.png in Resources */, @@ -3764,6 +3810,7 @@ 63CDC45E1C3BDE370085F529 /* msg.caf in Resources */, 633FEE6D1D3CD5590014B822 /* footer_dialer_disabled@2x.png in Resources */, 633FEF171D3CD55A0014B822 /* pause_small_disabled@2x.png in Resources */, + C6A1BB3626E8815400540D50 /* menu_forward_default.png in Resources */, D38187DD15FE348A00C3EDCA /* AssistantView.xib in Resources */, 633FEDA61D3CD5590014B822 /* back_disabled.png in Resources */, 633FEED61D3CD55A0014B822 /* numpad_7_over@2x.png in Resources */, @@ -3879,6 +3926,7 @@ C6B4444226AAD0980076C517 /* file_video_default.png in Resources */, 633FEDBE1D3CD5590014B822 /* call_back_default.png in Resources */, 633FEF0F1D3CD55A0014B822 /* pause_big_default@2x.png in Resources */, + C66B040E26F095D1009B5EDC /* cancel_forward.png in Resources */, CF7602F6210898CC00749F76 /* rec_on_default@2x.png in Resources */, 633FEF081D3CD55A0014B822 /* options_start_conference_disabled.png in Resources */, 63F1DF511BCE986A00EDED90 /* UICallConferenceCell.xib in Resources */, @@ -3908,6 +3956,7 @@ 633FEE221D3CD5590014B822 /* chat_start_body_over.png in Resources */, 633FEE601D3CD5590014B822 /* edit_list_disabled.png in Resources */, D38187C115FE345B00C3EDCA /* DialerView.xib in Resources */, + C6A1BB3726E8815400540D50 /* menu_copy_text_default.png in Resources */, D37EE10D16035793003608A6 /* ImageView.xib in Resources */, 633FEE9F1D3CD55A0014B822 /* numpad_0_over~ipad.png in Resources */, 633FEED51D3CD55A0014B822 /* numpad_7_over.png in Resources */, @@ -3923,6 +3972,7 @@ 633FEE4A1D3CD5590014B822 /* delete_disabled.png in Resources */, 614D09CE21E74D5400C43EDF /* GoogleService-Info.plist in Resources */, C6B4444526AAD0980076C517 /* file_audio_default.png in Resources */, + C6A1BB3526E8815400540D50 /* menu_info.png in Resources */, C61B1BF72667EC6B001A4E4A /* ephemeral_messages_color_A.png in Resources */, 633FEF151D3CD55A0014B822 /* pause_small_default@2x.png in Resources */, 633FEEF91D3CD55A0014B822 /* numpad_star_over~ipad@2x.png in Resources */, @@ -3997,6 +4047,7 @@ 633FEDDD1D3CD5590014B822 /* call_start_body_disabled~ipad@2x.png in Resources */, 633FEEBD1D3CD55A0014B822 /* numpad_4_over.png in Resources */, 8CA70AD41F9E285C00A3D2EB /* chat_group_add@2x.png in Resources */, + C66B040A26EFDA55009B5EDC /* reply_cancel.png in Resources */, 633FEEF11D3CD55A0014B822 /* numpad_hash~ipad.png in Resources */, 633FEE781D3CD5590014B822 /* history_chat_indicator.png in Resources */, 633FEF431D3CD55A0014B822 /* select_all_disabled@2x.png in Resources */, @@ -4275,6 +4326,7 @@ CF7602D7210867E800749F76 /* RecordingsListView.m in Sources */, 63F1DF4B1BCE983200EDED90 /* CallConferenceTableView.m in Sources */, D3F83F8E15822ABE00336684 /* PhoneMainView.m in Sources */, + C6A1BB3E26E882D000540D50 /* UIChatReplyBubbleView.m in Sources */, 6377AC801BDE4069007F7625 /* UIBackToCallButton.m in Sources */, 6308F9C51BF0DD6600D1234B /* XMLRPCHelper.m in Sources */, D3ED3E871586291E006C0DE4 /* TabBarView.m in Sources */, @@ -4764,6 +4816,16 @@ name = UIChatCreateCollectionViewCell.xib; sourceTree = ""; }; + C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */ = { + isa = PBXVariantGroup; + children = ( + C66B03BC26E8EB1A009B5EDC /* Base */, + C66B03C126E8EB82009B5EDC /* en */, + C66B03C326E8EB87009B5EDC /* fr */, + ); + name = UIChatReplyBubbleView.xib; + sourceTree = ""; + }; D37EE11016035793003608A6 /* ImageView.xib */ = { isa = PBXVariantGroup; children = ( @@ -4997,7 +5059,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.11+950fc62\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.50+724b895\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5120,7 +5182,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.11+950fc62\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.50+724b895\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5242,7 +5304,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.11+950fc62\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.50+724b895\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5363,7 +5425,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.11+950fc62\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.50+724b895\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;