From 50f0a56a4f6dea8d2d14d21759c21cd4ae241fc4 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Mon, 16 Nov 2015 11:05:06 +0100 Subject: [PATCH] ChatConversation: optimize conversation loading and fix UI tests --- Classes/Base.lproj/ChatConversationView.xib | 2 +- Classes/ChatConversationTableView.m | 7 +- Classes/ContactDetailsTableView.h | 4 +- Classes/ContactDetailsTableView.m | 79 +++++++++----------- Classes/LinphoneUI/UIChatBubblePhotoCell.h | 1 + Classes/LinphoneUI/UIChatBubblePhotoCell.m | 68 +++++++---------- Classes/LinphoneUI/UIChatBubblePhotoCell.xib | 3 +- Classes/LinphoneUI/UIChatBubbleTextCell.h | 6 +- Classes/LinphoneUI/UIChatBubbleTextCell.m | 76 ++++++++++++------- Classes/LinphoneUI/UIChatBubbleTextCell.xib | 2 +- Classes/SettingsView.m | 5 +- TestsUI/ChatTester.m | 21 +++--- 12 files changed, 146 insertions(+), 128 deletions(-) diff --git a/Classes/Base.lproj/ChatConversationView.xib b/Classes/Base.lproj/ChatConversationView.xib index 9013e94af..336e81e40 100644 --- a/Classes/Base.lproj/ChatConversationView.xib +++ b/Classes/Base.lproj/ChatConversationView.xib @@ -148,7 +148,7 @@ - + diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index 581d4f670..856ebf480 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -179,6 +179,9 @@ cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; } [cell setChatMessage:chat]; + if (chat) { + [cell update]; + } [cell setChatRoomDelegate:_chatRoomDelegate]; [super accessoryForCell:cell atPath:indexPath]; return cell; @@ -204,8 +207,8 @@ } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - UIChatBubbleTextCell *cell = (UIChatBubbleTextCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; - return [cell viewSizeWithWidth:self.view.frame.size.width].height; + LinphoneChatMessage *chat = ms_list_nth_data(messageList, (int)[indexPath row]); + return [UIChatBubbleTextCell ViewSizeForMessage:chat withWidth:self.view.frame.size.width].height; } diff --git a/Classes/ContactDetailsTableView.h b/Classes/ContactDetailsTableView.h index e521dc505..72777fc20 100644 --- a/Classes/ContactDetailsTableView.h +++ b/Classes/ContactDetailsTableView.h @@ -26,11 +26,11 @@ typedef enum _ContactSections { ContactSections_None = 0, // first section is empty because we cannot set header for first section ContactSections_First_Name, ContactSections_Last_Name, - ContactSections_Number, ContactSections_Sip, + ContactSections_Number, ContactSections_Email, ContactSections_MAX -} ContactSections_e; +} ContactSections; @interface ContactDetailsTableView : UITableViewController { @private diff --git a/Classes/ContactDetailsTableView.m b/Classes/ContactDetailsTableView.m index 9f0bd405d..abbd1f9a8 100644 --- a/Classes/ContactDetailsTableView.m +++ b/Classes/ContactDetailsTableView.m @@ -48,10 +48,6 @@ @implementation ContactDetailsTableView -static const ContactSections_e contactSections[ContactSections_MAX] = { - ContactSections_None, ContactSections_First_Name, ContactSections_Last_Name, - ContactSections_Sip, ContactSections_Number, ContactSections_Email}; - @synthesize contactDetailsDelegate; @synthesize contact; @@ -102,11 +98,11 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { } - (NSMutableArray *)getSectionData:(NSInteger)section { - if (contactSections[section] == ContactSections_Number) { + if (section == ContactSections_Number) { return [dataCache objectAtIndex:0]; - } else if (contactSections[section] == ContactSections_Sip) { + } else if (section == ContactSections_Sip) { return [dataCache objectAtIndex:1]; - } else if (contactSections[section] == ContactSections_Email) { + } else if (section == ContactSections_Email) { if ([[LinphoneManager instance] lpConfigBoolForKey:@"show_contacts_emails_preference"] == true) { return [dataCache objectAtIndex:2]; } else { @@ -116,7 +112,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { return nil; } -- (ABPropertyID)propertyIDForSection:(ContactSections_e)section { +- (ABPropertyID)propertyIDForSection:(ContactSections)section { switch (section) { case ContactSections_First_Name: return kABPersonFirstNameProperty; @@ -313,7 +309,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { NSUInteger count = [sectionArray count]; CFErrorRef error = NULL; bool added = TRUE; - if (contactSections[section] == ContactSections_Number) { + if (section == ContactSections_Number) { ABMultiValueIdentifier identifier; ABMultiValueRef lcMap = ABRecordCopyValue(contact, kABPersonPhoneProperty); ABMutableMultiValueRef lMap; @@ -336,7 +332,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { LOGI(@"Can't add entry: %@", [(__bridge NSError *)error localizedDescription]); } CFRelease(lMap); - } else if (contactSections[section] == ContactSections_Sip) { + } else if (section == ContactSections_Sip) { Entry *entry = [self setOrCreateSipContactEntry:nil withValue:value]; if (entry) { [sectionArray addObject:entry]; @@ -345,7 +341,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { added = false; LOGE(@"Can't add entry for value: %@", value); } - } else if (contactSections[section] == ContactSections_Email) { + } else if (section == ContactSections_Email) { ABMultiValueIdentifier identifier; ABMultiValueRef lcMap = ABRecordCopyValue(contact, kABPersonEmailProperty); ABMutableMultiValueRef lMap; @@ -392,7 +388,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { if (row >= 0) { Entry *entry = [sectionDict objectAtIndex:row]; - ABPropertyID property = [self propertyIDForSection:contactSections[section]]; + ABPropertyID property = [self propertyIDForSection:(ContactSections)section]; if (property != kABInvalidPropertyType) { ABMultiValueRef lMap = ABRecordCopyValue(contact, property); NSInteger index = ABMultiValueGetIndexForIdentifier(lMap, [entry identifier]); @@ -425,7 +421,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { - (void)removeEntry:(UITableView *)tableview path:(NSIndexPath *)indexPath animated:(BOOL)animated { NSMutableArray *sectionArray = [self getSectionData:[indexPath section]]; Entry *entry = [sectionArray objectAtIndex:[indexPath row]]; - ABPropertyID property = [self propertyIDForSection:contactSections[indexPath.section]]; + ABPropertyID property = [self propertyIDForSection:(ContactSections)indexPath.section]; if (property != kABInvalidPropertyType) { ABMultiValueRef lcMap = ABRecordCopyValue(contact, property); @@ -459,22 +455,22 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { } - (void)addPhoneField:(NSString *)number { - int i = 0; - while (i < ContactSections_MAX && contactSections[i] != ContactSections_Number) + ContactSections i = 0; + while (i != ContactSections_MAX && i != ContactSections_Number) ++i; [self addEntry:[self tableView] section:i animated:FALSE value:number]; } - (void)addSipField:(NSString *)address { - int i = 0; - while (i < ContactSections_MAX && contactSections[i] != ContactSections_Sip) + ContactSections i = 0; + while (i != ContactSections_MAX && i != ContactSections_Sip) ++i; [self addEntry:[self tableView] section:i animated:FALSE value:address]; } - (void)addEmailField:(NSString *)address { - int i = 0; - while (i < ContactSections_MAX && contactSections[i] != ContactSections_Email) + ContactSections i = 0; + while (i != ContactSections_MAX && i != ContactSections_Email) ++i; [self addEntry:[self tableView] section:i animated:FALSE value:address]; } @@ -486,8 +482,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if (contactSections[section] == ContactSections_First_Name || - contactSections[section] == ContactSections_Last_Name) { + if (section == ContactSections_First_Name || section == ContactSections_Last_Name) { return (self.tableView.isEditing) ? 1 : 0 /*no first and last name when not editting */; } else { return [[self getSectionData:section] count]; @@ -510,17 +505,17 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { NSString *label = [FastAddressBook localizedLabel:[labelArray objectAtIndex:0]]; [cell hideDeleteButton:NO]; - if (contactSections[indexPath.section] == ContactSections_First_Name) { + if (indexPath.section == ContactSections_First_Name) { value = (__bridge NSString *)(ABRecordCopyValue(contact, [self propertyIDForSection:ContactSections_First_Name])); label = nil; [cell hideDeleteButton:YES]; - } else if (contactSections[indexPath.section] == ContactSections_Last_Name) { + } else if (indexPath.section == ContactSections_Last_Name) { value = (__bridge NSString *)(ABRecordCopyValue(contact, [self propertyIDForSection:ContactSections_Last_Name])); label = nil; [cell hideDeleteButton:YES]; - } else if (contactSections[[indexPath section]] == ContactSections_Number) { + } else if ([indexPath section] == ContactSections_Number) { ABMultiValueRef lMap = ABRecordCopyValue(contact, kABPersonPhoneProperty); NSInteger index = ABMultiValueGetIndexForIdentifier(lMap, [entry identifier]); NSString *labelRef = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(lMap, index)); @@ -532,7 +527,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { value = [FastAddressBook localizedLabel:valueRef]; } CFRelease(lMap); - } else if (contactSections[[indexPath section]] == ContactSections_Sip) { + } else if ([indexPath section] == ContactSections_Sip) { ABMultiValueRef lMap = ABRecordCopyValue(contact, kABPersonInstantMessageProperty); NSInteger index = ABMultiValueGetIndexForIdentifier(lMap, [entry identifier]); @@ -556,7 +551,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { } CFRelease(lDict); CFRelease(lMap); - } else if (contactSections[[indexPath section]] == ContactSections_Email) { + } else if ([indexPath section] == ContactSections_Email) { ABMultiValueRef lMap = ABRecordCopyValue(contact, kABPersonEmailProperty); NSInteger index = ABMultiValueGetIndexForIdentifier(lMap, [entry identifier]); NSString *labelRef = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(lMap, index)); @@ -570,11 +565,11 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { CFRelease(lMap); } [cell setAddress:value]; - if (contactSections[[indexPath section]] == ContactSections_Number) { + if ([indexPath section] == ContactSections_Number) { [cell.editTextfield setKeyboardType:UIKeyboardTypePhonePad]; - } else if (contactSections[[indexPath section]] == ContactSections_Sip) { + } else if ([indexPath section] == ContactSections_Sip) { [cell.editTextfield setKeyboardType:UIKeyboardTypeASCIICapable]; - } else if (contactSections[[indexPath section]] == ContactSections_Email) { + } else if ([indexPath section] == ContactSections_Email) { [cell.editTextfield setKeyboardType:UIKeyboardTypeASCIICapable]; } else { [cell.editTextfield setKeyboardType:UIKeyboardTypeDefault]; @@ -588,7 +583,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { Entry *entry = [sectionDict objectAtIndex:[indexPath row]]; if ([self isEditing]) { NSString *key = nil; - ABPropertyID property = [self propertyIDForSection:contactSections[indexPath.section]]; + ABPropertyID property = [self propertyIDForSection:(ContactSections)indexPath.section]; if (property != kABInvalidPropertyType && property != kABPersonFirstNameProperty && property != kABPersonLastNameProperty) { @@ -629,8 +624,8 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { if (editing) { // add phone/SIP/email entries so that the user can add new data for (int section = 0; section < [self numberOfSectionsInTableView:[self tableView]]; ++section) { - if (contactSections[section] == ContactSections_Number || contactSections[section] == ContactSections_Sip || - (showEmails && contactSections[section] == ContactSections_Email)) { + if (section == ContactSections_Number || section == ContactSections_Sip || + (showEmails && section == ContactSections_Email)) { [self addEntry:self.tableView section:section animated:animated]; } } @@ -639,8 +634,8 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { // remove empty phone numbers for (int section = 0; section < [self numberOfSectionsInTableView:[self tableView]]; ++section) { // remove phony entries that were not filled by the user - if (contactSections[section] == ContactSections_Number || contactSections[section] == ContactSections_Sip || - (showEmails && contactSections[section] == ContactSections_Email)) { + if (section == ContactSections_Number || section == ContactSections_Sip || + (showEmails && section == ContactSections_Email)) { [self removeEmptyEntry:self.tableView section:section animated:animated]; if ([[self getSectionData:section] count] == 0 && animated) { // the section is empty -> remove titles @@ -665,20 +660,20 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { NSString *text = nil; BOOL canAddEntry = self.tableView.isEditing; NSString *addEntryName = nil; - if (contactSections[section] == ContactSections_First_Name && self.tableView.isEditing) { + if (section == ContactSections_First_Name && self.tableView.isEditing) { text = NSLocalizedString(@"First name", nil); canAddEntry = NO; - } else if (contactSections[section] == ContactSections_Last_Name && self.tableView.isEditing) { + } else if (section == ContactSections_Last_Name && self.tableView.isEditing) { text = NSLocalizedString(@"Last name", nil); canAddEntry = NO; } else if ([self getSectionData:section].count > 0 || self.tableView.isEditing) { - if (contactSections[section] == ContactSections_Number) { + if (section == ContactSections_Number) { text = NSLocalizedString(@"Phone numbers", nil); addEntryName = NSLocalizedString(@"Add new phone number", nil); - } else if (contactSections[section] == ContactSections_Sip) { + } else if (section == ContactSections_Sip) { text = NSLocalizedString(@"SIP addresses", nil); addEntryName = NSLocalizedString(@"Add new SIP address", nil); - } else if (contactSections[section] == ContactSections_Email && + } else if (section == ContactSections_Email && [LinphoneManager.instance lpConfigBoolForKey:@"show_contacts_emails_preference"]) { text = NSLocalizedString(@"Email addresses", nil); addEntryName = NSLocalizedString(@"Add new email", nil); @@ -780,7 +775,7 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { NSIndexPath *path = [self.tableView indexPathForCell:cell]; NSMutableArray *sectionDict = [self getSectionData:[path section]]; Entry *entry = [sectionDict objectAtIndex:[path row]]; - ContactSections_e sect = contactSections[[path section]]; + ContactSections sect = (ContactSections)[path section]; ABPropertyID property = [self propertyIDForSection:sect]; NSString *value = [textField text]; @@ -844,8 +839,8 @@ static const ContactSections_e contactSections[ContactSections_MAX] = { } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - if (section == 0 || (!self.tableView.isEditing && (contactSections[section] == ContactSections_First_Name || - contactSections[section] == ContactSections_Last_Name))) { + if (section == 0 || (!self.tableView.isEditing && + (section == ContactSections_First_Name || section == ContactSections_Last_Name))) { return 1e-5; } return [self tableView:tableView viewForHeaderInSection:section].frame.size.height; diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index 31d1b9a3c..f196dbcd1 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -33,6 +33,7 @@ @property(weak, nonatomic) IBOutlet UIButton *cancelButton; @property(weak, nonatomic) IBOutlet UIView *imageSubView; @property(weak, nonatomic) IBOutlet UIView *totalView; +@property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer; - (void)setChatMessage:(LinphoneChatMessage *)message; - (void)connectToFileDelegate:(FileTransferDelegate *)ftd; diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index a8b53e714..85f44023b 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -25,7 +25,7 @@ #import @implementation UIChatBubblePhotoCell { - FileTransferDelegate *ftd; + FileTransferDelegate *_ftd; } #pragma mark - Lifecycle Functions @@ -53,10 +53,7 @@ #pragma mark - - (void)setChatMessage:(LinphoneChatMessage *)amessage { - if (amessage == self.message) { - return; - } - + _imageGestureRecognizer.enabled = NO; _messageImageView.image = nil; _fileTransferProgress.progress = 0; [self disconnectFromFileDelegate]; @@ -70,10 +67,8 @@ (linphone_chat_message_is_outgoing(aftd.message) == linphone_chat_message_is_outgoing(amessage)) && strcmp(name, linphone_content_get_name( linphone_chat_message_get_file_transfer_information(aftd.message))) == 0) { - if (ftd != aftd) { - LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd); - [self connectToFileDelegate:aftd]; - } + LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd); + [self connectToFileDelegate:aftd]; break; } } @@ -113,6 +108,7 @@ [_messageImageView setFullImageUrl:asset]; [_messageImageView stopLoading]; _messageImageView.hidden = NO; + _imageGestureRecognizer.enabled = YES; }); } }); @@ -122,7 +118,7 @@ }]; } // we are uploading the image - if (ftd.message != nil) { + if (_ftd.message != nil) { _cancelButton.hidden = NO; _fileTransferProgress.hidden = NO; _downloadButton.hidden = YES; @@ -133,7 +129,7 @@ // we must download the image: either it has already started (show cancel button) or not yet (show download // button) } else { - _messageImageView.hidden = _cancelButton.hidden = (ftd.message == nil); + _messageImageView.hidden = _cancelButton.hidden = (_ftd.message == nil); _downloadButton.hidden = !_cancelButton.hidden; _fileTransferProgress.hidden = NO; @@ -156,20 +152,27 @@ } - (IBAction)onDownloadClick:(id)event { - [ftd cancel]; - ftd = [[FileTransferDelegate alloc] init]; - [self connectToFileDelegate:ftd]; - [ftd download:self.message]; + [_ftd cancel]; + _ftd = [[FileTransferDelegate alloc] init]; + [self connectToFileDelegate:_ftd]; + [_ftd download:self.message]; _cancelButton.hidden = NO; _downloadButton.hidden = YES; + + // we must tell the tableview to refresh the cell to reflect its internal state + ChatConversationView *view = VIEW(ChatConversationView); + [view.tableController updateChatEntry:self.message]; } - (IBAction)onCancelClick:(id)sender { - FileTransferDelegate *tmp = ftd; + FileTransferDelegate *tmp = _ftd; [self disconnectFromFileDelegate]; [tmp cancel]; _fileTransferProgress.progress = 0; [self update]; + // we must tell the tableview to refresh the cell to reflect its internal state + ChatConversationView *view = VIEW(ChatConversationView); + [view.tableController updateChatEntry:self.message]; } - (void)onResendClick:(id)event { @@ -194,22 +197,28 @@ #pragma mark - LinphoneFileTransfer Notifications Handling - (void)connectToFileDelegate:(FileTransferDelegate *)aftd { - ftd = aftd; + if (aftd.message && linphone_chat_message_get_state(aftd.message) == LinphoneChatMessageStateFileTransferError) { + LOGW(@"This file transfer failed unexpectedly, cleaning it"); + [aftd stopAndDestroy]; + return; + } + + _ftd = aftd; _fileTransferProgress.progress = 0; [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFileTransferSendUpdate:) name:kLinphoneFileTransferSendUpdate - object:ftd]; + object:_ftd]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFileTransferRecvUpdate:) name:kLinphoneFileTransferRecvUpdate - object:ftd]; + object:_ftd]; } - (void)disconnectFromFileDelegate { [[NSNotificationCenter defaultCenter] removeObserver:self]; - ftd = nil; + _ftd = nil; } - (void)onFileTransferSendUpdate:(NSNotification *)notif { @@ -241,23 +250,4 @@ } } -- (CGSize)viewSizeWithWidth:(int)width { - static const CGFloat IMAGE_HEIGHT = 100.0f; - static const CGFloat IMAGE_WIDTH = 100.0f; - - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message]; - CGSize messageSize = (localImage != nil) ? CGSizeMake(IMAGE_WIDTH, IMAGE_HEIGHT) : CGSizeMake(50, 50); - - CGSize dateSize = [self computeBoundingBox:self.contactDateLabel.text - size:self.contactDateLabel.frame.size - font:self.contactDateLabel.font]; - - CGSize bubbleSize; - bubbleSize.width = - MAX(messageSize.width, dateSize.width + self.statusErrorImage.frame.size.width + 5) + MARGIN_WIDTH; - bubbleSize.height = messageSize.height + MARGIN_HEIGHT; - - return bubbleSize; -} - @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.xib b/Classes/LinphoneUI/UIChatBubblePhotoCell.xib index 567b1088d..dfcba671f 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.xib +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.xib @@ -15,6 +15,7 @@ + @@ -119,7 +120,7 @@ - + diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h index dfeb87e88..adc558ce8 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.h +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -28,6 +28,8 @@ static const CGFloat TEXT_MIN_WIDTH = 150.0f; static const CGFloat MARGIN_WIDTH = 60; static const CGFloat MARGIN_HEIGHT = 19 + 16 /*this 16 is because textview add some top&bottom padding*/; static const CGFloat CHECK_BOX_WIDTH = 40; +static const CGFloat IMAGE_HEIGHT = 100.0f; +static const CGFloat IMAGE_WIDTH = 100.0f; @interface UIChatBubbleTextCell : UITableViewCell @@ -43,7 +45,7 @@ static const CGFloat CHECK_BOX_WIDTH = 40; @property(strong, nonatomic) IBOutlet UIView *bubbleView; @property(strong, nonatomic) IBOutlet UITapGestureRecognizer *resendRecognizer; -- (CGSize)viewSizeWithWidth:(int)width; ++ (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width; - (void)setChatMessage:(LinphoneChatMessage *)message; @@ -52,6 +54,6 @@ static const CGFloat CHECK_BOX_WIDTH = 40; - (void)update; + (NSString *)TextMessageForChat:(LinphoneChatMessage *)message; -- (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font; ++ (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font; @end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 7e126ea57..d5d21a031 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -30,13 +30,15 @@ - (id)initWithIdentifier:(NSString *)identifier { if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) { - NSArray *arrayOfViews = - [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; - // resize cell to match .nib size. It is needed when resized the cell to - // correctly adapt its height too - UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:arrayOfViews.count - 1]); - [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; - [self addSubview:sub]; + if ([identifier isEqualToString:NSStringFromClass(self.class)]) { + NSArray *arrayOfViews = + [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; + // resize cell to match .nib size. It is needed when resized the cell to + // correctly adapt its height too + UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:arrayOfViews.count - 1]); + [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; + [self addSubview:sub]; + } } return self; } @@ -63,16 +65,22 @@ linphone_chat_message_ref(_message); linphone_chat_message_set_user_data(_message, (void *)CFBridgingRetain(self)); linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(_message), message_status); - [self update]; } } -+ (NSString *)TextMessageForChat:(LinphoneChatMessage *)_message { - const char *text = linphone_chat_message_get_text(_message) ?: ""; ++ (NSString *)TextMessageForChat:(LinphoneChatMessage *)message { + const char *text = linphone_chat_message_get_text(message) ?: ""; return [NSString stringWithUTF8String:text] ?: [NSString stringWithCString:text encoding:NSASCIIStringEncoding] ?: NSLocalizedString(@"(invalid string)", nil); } ++ (NSString *)ContactDateForChat:(LinphoneChatMessage *)message { + return [NSString + stringWithFormat:@"%@ - %@", [LinphoneUtils timeToString:linphone_chat_message_get_time(message) + withFormat:LinphoneDateChatBubble], + [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(message)]]; +} + - (NSString *)textMessage { return [self.class TextMessageForChat:_message]; } @@ -83,6 +91,8 @@ return; } + _statusInProgressSpinner.accessibilityLabel = @"Delivery in progress"; + if (_messageText) { [_messageText setHidden:FALSE]; /* We need to use an attributed string here so that data detector don't mess @@ -108,10 +118,7 @@ bordered:NO withRoundedRadius:YES]; } - _contactDateLabel.text = [NSString - stringWithFormat:@"%@ - %@", [LinphoneUtils timeToString:linphone_chat_message_get_time(_message) - withFormat:LinphoneDateChatBubble], - [FastAddressBook displayNameForAddress:linphone_chat_message_get_from_address(_message)]]; + _contactDateLabel.text = [self.class ContactDateForChat:_message]; _backgroundColorImage.image = _bottomBarColor.image = [UIImage imageNamed:(outgoing ? @"color_A.png" : @"color_D.png")]; @@ -165,7 +172,7 @@ return; LinphoneChatMessageState state = linphone_chat_message_get_state(_message); - if (state == LinphoneChatMessageStateNotDelivered) { + if (state == LinphoneChatMessageStateNotDelivered || state == LinphoneChatMessageStateFileTransferError) { if (linphone_chat_message_get_file_transfer_information(_message) != NULL) { NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message]; NSURL *imageUrl = [NSURL URLWithString:localImage]; @@ -204,7 +211,7 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st #pragma mark - Bubble size computing -- (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font { ++ (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font { if (!text || text.length == 0) return CGSizeMake(0, 0); @@ -222,21 +229,36 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st { return [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping]; } } -- (CGSize)viewSizeWithWidth:(int)width { ++ (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width { + static UIFont *messageFont = nil; + static UIFont *dateFont = nil; + static CGSize dateViewSize; + int messageAvailableWidth = width - MARGIN_WIDTH - CHECK_BOX_WIDTH; + CGSize messageSize; - NSString *text = [UIChatBubbleTextCell TextMessageForChat:_message]; + if (!messageFont) { + UIChatBubbleTextCell *cell = + [[UIChatBubbleTextCell alloc] initWithIdentifier:NSStringFromClass(UIChatBubbleTextCell.class)]; + messageFont = cell.messageText.font; + dateFont = cell.contactDateLabel.font; + dateViewSize = cell.contactDateLabel.frame.size; + } - CGSize messageSize = - [self computeBoundingBox:text size:CGSizeMake(messageAvailableWidth, CGFLOAT_MAX) font:_messageText.font]; - messageSize.width = MAX(TEXT_MIN_WIDTH, ceil(messageSize.width)); - messageSize.height = MAX(TEXT_MIN_HEIGHT, ceil(messageSize.height)); - - CGSize dateSize = - [self computeBoundingBox:_contactDateLabel.text size:_contactDateLabel.frame.size font:_contactDateLabel.font]; + if (linphone_chat_message_get_file_transfer_information(chat)) { + NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat]; + messageSize = (localImage != nil) ? CGSizeMake(IMAGE_WIDTH, IMAGE_HEIGHT) : CGSizeMake(50, 50); + } else { + NSString *text = [UIChatBubbleTextCell TextMessageForChat:chat]; + messageSize = + [self computeBoundingBox:text size:CGSizeMake(messageAvailableWidth, CGFLOAT_MAX) font:messageFont]; + messageSize.width = MAX(TEXT_MIN_WIDTH, ceil(messageSize.width)); + messageSize.height = MAX(TEXT_MIN_HEIGHT, ceil(messageSize.height)); + } + CGSize dateSize = [self computeBoundingBox:[self ContactDateForChat:chat] size:dateViewSize font:dateFont]; CGSize bubbleSize; - bubbleSize.width = MAX(messageSize.width, dateSize.width + _statusErrorImage.frame.size.width + 5) + MARGIN_WIDTH; + bubbleSize.width = MAX(messageSize.width, dateSize.width + 20 /*error icon*/ + 5) + MARGIN_WIDTH; bubbleSize.height = messageSize.height + MARGIN_HEIGHT; return bubbleSize; @@ -248,7 +270,7 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st UITableView *tableView = VIEW(ChatConversationView).tableController.tableView; BOOL is_outgoing = linphone_chat_message_is_outgoing(_message); CGRect bubbleFrame = _bubbleView.frame; - bubbleFrame.size = [self viewSizeWithWidth:self.frame.size.width]; + bubbleFrame.size = [self.class ViewSizeForMessage:_message withWidth:self.frame.size.width]; bubbleFrame.origin.x = tableView.isEditing ? 0 : (is_outgoing ? self.frame.size.width - bubbleFrame.size.width : 0); _bubbleView.frame = bubbleFrame; diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.xib b/Classes/LinphoneUI/UIChatBubbleTextCell.xib index 52153f10c..86fada1a5 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.xib +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.xib @@ -67,7 +67,7 @@ - + diff --git a/Classes/SettingsView.m b/Classes/SettingsView.m index fa63981ff..a627fcc29 100644 --- a/Classes/SettingsView.m +++ b/Classes/SettingsView.m @@ -685,7 +685,10 @@ static UICompositeViewDescription *compositeDescription = nil; while (rooms) { const MSList *messages = linphone_chat_room_get_history(rooms->data, 0); while (messages) { - [LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:messages->data]; + LinphoneChatMessage *msg = messages->data; + if (!linphone_chat_message_is_outgoing(msg)) { + [LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:messages->data]; + } messages = messages->next; } rooms = rooms->next; diff --git a/TestsUI/ChatTester.m b/TestsUI/ChatTester.m index ca64d5cab..214b05317 100644 --- a/TestsUI/ChatTester.m +++ b/TestsUI/ChatTester.m @@ -22,9 +22,7 @@ - (void)beforeEach { [super beforeEach]; - if ([tester tryFindingTappableViewWithAccessibilityLabel:@"Back" error:nil]) { - [self goBackFromChat]; - } + [self goBackFromChat]; [tester tapViewWithAccessibilityLabel:@"Chat"]; [self removeAllRooms]; } @@ -33,9 +31,7 @@ [super afterAll]; linphone_core_set_log_level(ORTP_MESSAGE); // at the end of tests, go back to chat rooms to display main bar - if ([tester tryFindingTappableViewWithAccessibilityLabel:@"Back" error:nil]) { - [self goBackFromChat]; - } + [self goBackFromChat]; ASSERT_EQ([LinphoneManager instance].fileTransferDelegates.count, 0) } @@ -51,8 +47,14 @@ [tester tapViewWithAccessibilityLabel:@"DELETE" traits:UIAccessibilityTraitButton]; } +- (void)dismissKeyboard { + [tester tapScreenAtPoint:CGPointMake(0, 0)]; // dismiss keyboard, if any +} - (void)goBackFromChat { - [tester tapViewWithAccessibilityLabel:@"Back"]; + [self dismissKeyboard]; + if ([tester tryFindingTappableViewWithAccessibilityLabel:@"Back" error:nil]) { + [tester tapViewWithAccessibilityLabel:@"Back"]; + } } - (void)startChatWith:(NSString *)user { @@ -135,9 +137,8 @@ [self startChatWith:user]; [self sendMessage:user]; - [tester waitForViewWithAccessibilityLabel:@"Message status" - value:@"not delivered" - traits:UIAccessibilityTraitImage]; + [tester waitForViewWithAccessibilityLabel:@"Delivery failed" traits:UIAccessibilityTraitImage]; + [self dismissKeyboard]; [tester tapViewWithAccessibilityLabel:@"Edit" traits:UIAccessibilityTraitButton]; [tester waitForViewWithAccessibilityLabel:@"Checkbox" value:@"Deselected" traits:UIAccessibilityTraitButton];