From 0d55c0ca2ff2105f3ca5959143479788f7497bbf Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Mon, 26 Jul 2021 15:20:02 +0200 Subject: [PATCH] Copy/Transfer/Reply feature for chat messages --- .../Base.lproj/ChatConversationImdnView.xib | 76 +--- Classes/Base.lproj/ChatConversationView.xib | 6 + Classes/Base.lproj/ChatsListView.xib | 76 +++- Classes/ChatConversationImdnView.h | 8 +- Classes/ChatConversationImdnView.m | 100 +++-- Classes/ChatConversationTableView.h | 12 +- Classes/ChatConversationTableView.m | 47 +++ Classes/ChatConversationView.h | 15 +- Classes/ChatConversationView.m | 204 ++++++--- Classes/ChatsListTableView.h | 1 + Classes/ChatsListTableView.m | 7 + Classes/ChatsListView.h | 3 + Classes/ChatsListView.m | 25 +- .../Base.lproj/UIChatBubblePhotoCell.xib | 302 ++++++------- .../Base.lproj/UIChatBubbleTextCell.xib | 14 + Classes/LinphoneUI/Base.lproj/UIChatCell.xib | 14 +- .../Base.lproj/UIChatReplyBubbleView.xib | 84 ++++ .../Base.lproj/UIConfirmationDialog.xib | 40 +- Classes/LinphoneUI/UIChatBubblePhotoCell.h | 3 - Classes/LinphoneUI/UIChatBubblePhotoCell.m | 59 ++- Classes/LinphoneUI/UIChatBubbleTextCell.h | 24 +- Classes/LinphoneUI/UIChatBubbleTextCell.m | 395 ++++++++++++------ Classes/LinphoneUI/UIChatCell.h | 2 + Classes/LinphoneUI/UIChatCell.m | 1 + Classes/LinphoneUI/UIChatReplyBubbleView.h | 40 ++ Classes/LinphoneUI/UIChatReplyBubbleView.m | 159 +++++++ Classes/LinphoneUI/UIConfirmationDialog.h | 1 + .../en.lproj/UIChatReplyBubbleView.strings | 9 + .../fr.lproj/UIChatReplyBubbleView.strings | 9 + Classes/PhoneMainView.h | 1 + Classes/PhoneMainView.m | 11 + Classes/SwiftUtil.swift | 69 ++- Classes/Utils/FileTransferDelegate.h | 7 +- Classes/Utils/FileTransferDelegate.m | 25 +- Classes/Utils/Utils.h | 9 + Classes/Utils/Utils.m | 30 ++ Podfile | 2 +- Resources/fr.lproj/Localizable.strings | Bin 57484 -> 29538 bytes Resources/images/cancel_forward.png | Bin 0 -> 9030 bytes Resources/images/file_voice_default.png | Bin 0 -> 8605 bytes Resources/images/forward_message_default.png | Bin 0 -> 3732 bytes Resources/images/menu_copy_text_default.png | Bin 0 -> 1899 bytes Resources/images/menu_delete.png | Bin 0 -> 729 bytes Resources/images/menu_forward_default.png | Bin 0 -> 1910 bytes Resources/images/menu_info.png | Bin 0 -> 2069 bytes Resources/images/menu_reply_default.png | Bin 0 -> 9498 bytes Resources/images/menu_resend_default.png | Bin 0 -> 8229 bytes Resources/images/reply_cancel.png | Bin 0 -> 2884 bytes linphone.xcodeproj/project.pbxproj | 70 +++- 49 files changed, 1396 insertions(+), 564 deletions(-) create mode 100644 Classes/LinphoneUI/Base.lproj/UIChatReplyBubbleView.xib create mode 100644 Classes/LinphoneUI/UIChatReplyBubbleView.h create mode 100644 Classes/LinphoneUI/UIChatReplyBubbleView.m create mode 100644 Classes/LinphoneUI/en.lproj/UIChatReplyBubbleView.strings create mode 100644 Classes/LinphoneUI/fr.lproj/UIChatReplyBubbleView.strings create mode 100644 Resources/images/cancel_forward.png create mode 100644 Resources/images/file_voice_default.png create mode 100644 Resources/images/forward_message_default.png create mode 100644 Resources/images/menu_copy_text_default.png create mode 100644 Resources/images/menu_delete.png create mode 100644 Resources/images/menu_forward_default.png create mode 100644 Resources/images/menu_info.png create mode 100644 Resources/images/menu_reply_default.png create mode 100644 Resources/images/menu_resend_default.png create mode 100644 Resources/images/reply_cancel.png 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 318063fa67dcd23b218657727217bd4acbf5c768..42321344b9eb946a3366888fca31bb9287c3b85e 100644 GIT binary patch literal 29538 zcmd^|-E!PUlJB2j-T|Xn2ki}!3h(Td&e$WGqDJ(RvX-Rm@Cik1fZZSos~czo4N}zA z-tHsp-Q2A;ufaDtPjY_$%qpPJ-K6N4@y7Ysh*>v*LRM8)R^~rHs`M!PvF@5IpG@km zT4rUNO`0NK7So;Vha$VouZpZKc#&t5x;ihLdA=;`Dl6uBIm?!H_Sv(Y?Dee3+ajA> z7L#|`jmYV3tJhcgteis5ABSFcvhS)%k(Jinw62QB5T6&>S7NgF;{Kse8CKQ3=SGVI%rw;y-1S9RBB`Bh!DSy5fpH@xcN zc&GX*nZ@m2ve{NXZHkuRKh<2`RUg-wo4}!Bsd@a|L92!CjG5G(&E$Xl?$Kw@vK%tkO}Q+qi=7>x9#3yS zPMY!xw(FTUyv*C|r@E|)sqsLc(w&|xMfIYoyG4<-ofYTHq2gs|iE9vm>zWj%0Z>vN0L*5}Z2p=SP`%nMyOkaIoRwg_d zV!@(*Vc&c{N5b+8MBZN(U(ob-PyR96_ia=z8QOW>WQ(F+@bfyaBoP`nn{UcepLG{3 z_o8l=**`w{-J?H-EjKM%B;_n@uSHWc$9V=Jc42mnux7j~FRHpJreE7o{`foy*M}h7 z_R;MxY`=0+KF->*V!|%>zRBC7$(_dS$FDWTx7A*{iP~b)A;C9qtGCrjej`TpE!eyH z()T`sS~RnIg7i0Ut2VzGSju+{N<9}^{GF$pZmx5 zcrqy#p|$-in()|^GyCSeyyzNN!DZWag^_rzb+;vq3a)Q7?Sh_6=@jo5rG(QyJ{&Y@ zBc-FcSOe?DKATRBaV8EvW+uyOqFC8GL4EpL%{Vk)T%y^ts+*rN3+e6M{wqDb{n)@! z7=8P3cKgv9r=6V~yoR*VUj2A-na>w}zcdWrNsXgk>v#S^RdspU_#!wVwx5i~tXMkV zcEixadT3)2(h< z&dL^yq5ZcEujVzDroa$m+2b%SXmg$4`1taU{m#y{7EOU!_hw7v`bPSo>8eUPn7zM~ zoh<8x+YXp5Sw{b#&JA~UXB>%aZCUg03h3TX~xoD0u?}ttXtibAHraG!?NYP0z63h zm+kFGW43>@1zai`w5XJ_QGeb4*{^Ppe<$U$skWBcd5LjcUb0%}Ox`{|n4@A_Erux< zV37F0;0R<8q^tbm{&JU1bMqDmXCo{hNzc_Vt$dA703ix+0?sB5q7XH%@{J% zwk{#jgH>>`(8mp9G^Do78xWZ-vW*qEKe;q}hoy&EPqU�W<-hgZUz)3Hn(00=qiT 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 B?;ZdE literal 57484 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 diff --git a/Resources/images/cancel_forward.png b/Resources/images/cancel_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..8043430fea59b053066d668f0a2997665f25e688 GIT binary patch literal 9030 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 OAcB>>Wx2Ue^8W!G54Feu literal 0 HcmV?d00001 diff --git a/Resources/images/file_voice_default.png b/Resources/images/file_voice_default.png new file mode 100644 index 0000000000000000000000000000000000000000..a492d31a5d0ee22e138b9fb34d1e80d61029eef1 GIT binary patch literal 8605 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 V*ioj|%~AqDSNnukzUHaG{{kLLEu8=W literal 0 HcmV?d00001 diff --git a/Resources/images/forward_message_default.png b/Resources/images/forward_message_default.png new file mode 100644 index 0000000000000000000000000000000000000000..b51542ec8cbeb4c074a3bfba2864aab3cbecbb69 GIT binary patch literal 3732 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 literal 0 HcmV?d00001 diff --git a/Resources/images/menu_forward_default.png b/Resources/images/menu_forward_default.png new file mode 100644 index 0000000000000000000000000000000000000000..910450bf9fbc0a091f529b26aa125c040487a93e GIT binary patch literal 1910 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 Bjv4>} literal 0 HcmV?d00001 diff --git a/Resources/images/menu_reply_default.png b/Resources/images/menu_reply_default.png new file mode 100644 index 0000000000000000000000000000000000000000..8aa5b5e1e0df769b1f684fc4788e8d94170a6aa2 GIT binary patch literal 9498 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 literal 0 HcmV?d00001 diff --git a/Resources/images/menu_resend_default.png b/Resources/images/menu_resend_default.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5ab4ddd4264a772202482f47b7fdf74c075ebb GIT binary patch literal 8229 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 PXo0XM`;ChXk464JQ^NjN literal 0 HcmV?d00001 diff --git a/Resources/images/reply_cancel.png b/Resources/images/reply_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..8156bd6298c949ef93eba808588863a1b0b78029 GIT binary patch literal 2884 zcmV-K3%m4*P)EX>4Tx04R}tkv&MmKpe$iQ>AKK9PA+CkfAzR5EXIMDionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=H{g6A|?JWEwqU7;J6>}?mh0_0YbgZG^=YI&~)2O zCE{WxyDA1=(Tgw!5JXUBmN6$uNqCO0d-(Wz7vovp=l&dFHD@uvClb#x!?cMvh^IGg zgY!OdgcW6#_?&pmqze*1a$RZi8|Q+{0?&+?>C`-Ngjg(eu+qV-Xlle$#8Fk#DPPDm zS>?RNSu0mr>z@3Dp`5<5%ypW>NMI35kRU=q6(y8mBSx!EiiH&I$36T*u3sXTLaq`R zITlcX2HEw4|H1EWt^DLfGbt1Yx?ddUV+0870*#vEd>=bb;{@gF zj20++-Q(RooxS~grq$mM3Sn}6n*Cq|00006VoOIv0001%06=yDKCJ)%010qNS#tmY z3ljhU3ljkVnw%H_000McNlirueSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00|sPL_t(&-rZYma8y?re%^Bv!%SO4XDC?64#XeI=uo3G zfhLhm?SzjsRq~;bHc%?naXQLazHDh7O5-3crcpc88LT!)h$h)YBt$G29j3OF7?o`J zVa84y9AI}^WtY@c<0SXI{30ZV5Mu9*A0E6g^!2|CU&QF>-|+V3FPuib#+KoKd-fMh_htER7jttLrg>s3oR`KlZA#+yp>#d z{gehA)`o=W_{8Drx`sHgM3j`0IRUPV76?!Q{OIKlR5xto==j88t^J1JG1vpJ?%_oJ zfDnH7A6)l96aF}SNk{4w8kz-f$8~-NR(^T~PW+++ix(~+aAI)~-cQlMM91H+Z)n_% zp^KN|USdoHc*ZL8!ojw6_pC<$iEdI@wf&vk1Ew%}4+2zDuzlAa3#{9^Hn4G)&ml1TyU4t( zF`OZA=G-5!eb*jVNwH0S>@%ilSi7!kml}=W$O|n@I%z=qh1t8v{#{co^FAf0|KlIF zuo{hMxT0aGG40cuc&i7QSyhgS$tkBIrBEj#@>E5Chotl?+%_*Cp~3Sg4lE&S zo?&p=q%z^j{mpoPa>{!yJWGW$svrOeOiWJU$^HAG>fOMch-j3)3N;o+U$BGovZP%g z5a3`i=pU8!t`rOgIS>fM)7D09=WoFRib?IhU^}ZZtIL{zr(+RRmaX*cH|c;4g+eT) zG(o!?l+D%C)5FZnp-?CeG$r|N|6N&D%G0q3IC|9yxWB8N72QHQ0Wtz>FMfDB7z{d| z8488CsHiBVs5yTNRsinrYG*^3H3f@CP+wi8SM&z#<*`5@z#}6g>{YtEyZr$xouEDG z?fD!H2LW7nUzP4<9q^kYf424A^gZnMf*+s_8Ye25u}1@}0Q~025UbIM2KZp}J_ilb z1FY5Q?&*t=?X8^v6ii!$f#N}o{rwcY+b$8!eUAB^^V-aBd@+kZm zMs5bQZ(x%ERsg9Ga6`54ll^pnd za5K7~7S^)4upn;tfPNDWv4xUcItQ|J9zZV581X){~xHWjUEcXk8ax>M`aI6JByaK^dO0wcy6SV4bJn|6_pV0Y$==h87u`X?w3D&y^hn&WWIwQBzb{E(`_6Ci zk+$Pdks=|SvdKLXiL?D|N}**23V^4z^x=m$<8bStw0oQ_*AhscSx!(%;N@fOyzBA3 zn3$Z>FgP;eW!99;BtMG)+)|K-)1J!0-5_wxh0NJa8TUfOR~_%?#tUM)%O`k#UTtx(eGK-iq~W*Af}0SvV6qd5RWN z1GVOpnpJHtm54v6#U92_x1FCYFGbC|wfJIftv*GxP3ma|PNp$4NQjg#GLGVnkN>Ow z{Dlj>7egZ$9UaBnV}C~^5sR}`}msO4B&rLW?M~e!tgWz0000