diff --git a/Classes/Base.lproj/ChatConversationView.xib b/Classes/Base.lproj/ChatConversationView.xib index f9e64be51..7132776dc 100644 --- a/Classes/Base.lproj/ChatConversationView.xib +++ b/Classes/Base.lproj/ChatConversationView.xib @@ -1,7 +1,6 @@ - diff --git a/Classes/Base.lproj/ChatsListView.xib b/Classes/Base.lproj/ChatsListView.xib index 7fa0676fd..49a7b0ec4 100644 --- a/Classes/Base.lproj/ChatsListView.xib +++ b/Classes/Base.lproj/ChatsListView.xib @@ -1,7 +1,6 @@ - diff --git a/Classes/Base.lproj/ContactsListView.xib b/Classes/Base.lproj/ContactsListView.xib index 09ef98bd4..6044d1d69 100644 --- a/Classes/Base.lproj/ContactsListView.xib +++ b/Classes/Base.lproj/ContactsListView.xib @@ -1,7 +1,6 @@ - diff --git a/Classes/Base.lproj/HistoryListView.xib b/Classes/Base.lproj/HistoryListView.xib index d0f27cadb..8e8e365ea 100644 --- a/Classes/Base.lproj/HistoryListView.xib +++ b/Classes/Base.lproj/HistoryListView.xib @@ -1,7 +1,6 @@ - diff --git a/Classes/ChatConversationCreateTableView.m b/Classes/ChatConversationCreateTableView.m index 11496ab6e..a2adecf8f 100644 --- a/Classes/ChatConversationCreateTableView.m +++ b/Classes/ChatConversationCreateTableView.m @@ -40,8 +40,12 @@ } } // also add current entry, if not listed - if (![_contacts containsObject:filter]) { - [_contacts insertObject:filter atIndex:0]; + const LinphoneAddress *addr = linphone_core_interpret_url([LinphoneManager getLc], filter.UTF8String); + char *uri = linphone_address_as_string(addr); + NSString *nsuri = [NSString stringWithUTF8String:uri]; + ms_free(uri); + if (![_contacts containsObject:nsuri]) { + [_contacts insertObject:nsuri atIndex:0]; } } @@ -64,11 +68,17 @@ cell = [[UIChatCreateCell alloc] initWithIdentifier:kCellId]; } - cell.addressLabel.text = _contacts[indexPath.row]; const LinphoneAddress *addr = - linphone_core_interpret_url([LinphoneManager getLc], cell.addressLabel.text.UTF8String); - [ContactDisplay setDisplayNameLabel:cell.displayNameLabel forAddress:addr]; - + linphone_core_interpret_url([LinphoneManager getLc], ((NSString *)_contacts[indexPath.row]).UTF8String); + if (addr) { + char *uri = linphone_address_as_string(addr); + cell.addressLabel.text = [NSString stringWithUTF8String:uri]; + ms_free(uri); + [ContactDisplay setDisplayNameLabel:cell.displayNameLabel forAddress:addr]; + } else { + cell.displayNameLabel.text = _contacts[indexPath.row]; + cell.addressLabel.text = NSLocalizedString(@"Invalid address", nil); + } return cell; } diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index 900e4f8b6..581d4f670 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -25,8 +25,6 @@ @implementation ChatConversationTableView -@synthesize chatRoomDelegate; - #pragma mark - Lifecycle Functions - (void)dealloc { @@ -181,7 +179,7 @@ cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; } [cell setChatMessage:chat]; - [cell setChatRoomDelegate:chatRoomDelegate]; + [cell setChatRoomDelegate:_chatRoomDelegate]; [super accessoryForCell:cell atPath:indexPath]; return cell; } diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index dfb2caa80..f0788fdc0 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -23,31 +23,21 @@ #import "UITextViewNoDefine.h" #import "FileTransferDelegate.h" #import "ChatConversationTableView.h" +#import "UIChatBubbleTextCell.h" -@interface UIChatBubblePhotoCell : UITableViewCell +@interface UIChatBubblePhotoCell : UIChatBubbleTextCell -//@property(nonatomic, strong) IBOutlet UIView *innerView; -//@property(nonatomic, strong) IBOutlet UIView *bubbleView; -//@property(nonatomic, strong) IBOutlet UIImageView *backgroundImage; -//@property(nonatomic, strong) IBOutlet UITextViewNoDefine *messageText; -//@property(nonatomic, strong) IBOutlet UILoadingImageView *messageImageView; -//@property(nonatomic, strong) IBOutlet UIButton *deleteButton; -//@property(nonatomic, strong) IBOutlet UILabel *dateLabel; -//@property(nonatomic, strong) IBOutlet UIImageView *statusImage; -//@property(nonatomic, strong) IBOutlet UIButton *downloadButton; -//@property(weak, nonatomic) IBOutlet UIProgressView *fileTransferProgress; -//@property(weak, nonatomic) IBOutlet UIButton *cancelButton; -//@property(nonatomic, strong) id chatRoomDelegate; -// -//+ (CGFloat)height:(LinphoneChatMessage *)chatMessage width:(int)width; -// -//- (void)setChatMessage:(LinphoneChatMessage *)message; -//- (void)connectToFileDelegate:(FileTransferDelegate *)ftd; -// -//- (IBAction)onDeleteClick:(id)event; -//- (IBAction)onDownloadClick:(id)event; -//- (IBAction)onImageClick:(id)event; -//- (IBAction)onCancelDownloadClick:(id)sender; -//- (IBAction)onResendClick:(id)event; +@property(nonatomic, strong) IBOutlet UILoadingImageView *messageImageView; +@property(nonatomic, strong) IBOutlet UIButton *downloadButton; +@property(weak, nonatomic) IBOutlet UIProgressView *fileTransferProgress; +@property(weak, nonatomic) IBOutlet UIButton *cancelButton; +@property(weak, nonatomic) IBOutlet UIView *imageSubView; + +- (void)setChatMessage:(LinphoneChatMessage *)message; +- (void)connectToFileDelegate:(FileTransferDelegate *)ftd; +- (IBAction)onDownloadClick:(id)event; +- (IBAction)onImageClick:(id)event; +- (IBAction)onCancelClick:(id)sender; +- (IBAction)onResendClick:(id)event; @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index cae51760a..4215fc659 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -25,247 +25,124 @@ #import @implementation UIChatBubblePhotoCell { - LinphoneChatMessage *message; FileTransferDelegate *ftd; } -#if 0 - - -static const CGFloat CELL_MIN_HEIGHT = 50.0f; -static const CGFloat CELL_MIN_WIDTH = 150.0f; -static const CGFloat CELL_MESSAGE_X_MARGIN = 26.0f + 10.0f; -static const CGFloat CELL_MESSAGE_Y_MARGIN = 36.0f; -static const CGFloat CELL_FONT_SIZE = 17.0f; -static const CGFloat CELL_IMAGE_HEIGHT = 100.0f; -static const CGFloat CELL_IMAGE_WIDTH = 100.0f; -static UIFont *CELL_FONT = nil; #pragma mark - Lifecycle Functions - (id)initWithIdentifier:(NSString *)identifier { if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) { - [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; - -#if 0 - // shift message box, otherwise it will collide with the bubble - CGRect messageCoords = _messageText.frame; - messageCoords.origin.x += 2; - messageCoords.origin.y += 2; - messageCoords.size.width -= 5; - - _messageText.frame = messageCoords; - _messageText.allowSelectAll = TRUE; -#endif + // TODO: remove text cell subview + 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 = nil; + for (int i = 0; i < arrayOfViews.count; i++) { + if ([arrayOfViews[i] isKindOfClass:UIView.class]) { + sub = arrayOfViews[i]; + break; + } + } + [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; + [self addSubview:sub]; } - return self; } -- (void)dealloc { - [self setChatMessage:NULL]; -} - #pragma mark - - (void)setChatMessage:(LinphoneChatMessage *)amessage { - if (amessage == message) { + if (amessage == self.message) { return; } [self disconnectFromFileDelegate]; _messageImageView.image = nil; + _fileTransferProgress.progress = 0; - if (message) { - linphone_chat_message_unref(message); - linphone_chat_message_set_user_data(message, NULL); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(message), NULL); - } - - message = amessage; if (amessage) { - 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); - - const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(message); + const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(amessage); if (c) { const char *name = linphone_content_get_name(c); for (FileTransferDelegate *aftd in [[LinphoneManager instance] fileTransferDelegates]) { if (linphone_chat_message_get_file_transfer_information(aftd.message) && strcmp(name, linphone_content_get_name( - linphone_chat_message_get_file_transfer_information(aftd.message))) == 0) { - LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", message, aftd); + linphone_chat_message_get_file_transfer_information(amessage))) == 0) { + LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd); [self connectToFileDelegate:aftd]; break; } } } - [self update]; } -} - -+ (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 *)textMessage { - return [self.class TextMessageForChat:message]; + [super setChatMessage:amessage]; } - (void)update { - if (message == nil) { - LOGW(@"Cannot update message room cell: null message"); + [super update]; + if (self.message == nil) { + LOGW(@"Cannot update message room cell: NULL message"); return; } - const char *url = linphone_chat_message_get_external_body_url(message); + + LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); + if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateNotDelivered) { + if (ftd) { + [ftd stopAndDestroy]; + ftd = nil; + } + } + + const char *url = linphone_chat_message_get_external_body_url(self.message); BOOL is_external = - (url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(message); - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:message]; + (url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message); + NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message]; - // this is an image (either to download or already downloaded) - if (is_external || localImage) { - if (localImage) { - if (_messageImageView.image == nil) { - NSURL *imageUrl = [NSURL URLWithString:localImage]; - _messageText.hidden = YES; - [_messageImageView startLoading]; - __block LinphoneChatMessage *achat = message; - [LinphoneManager.instance.photoLibrary assetForURL:imageUrl - resultBlock:^(ALAsset *asset) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), - ^(void) { - if (achat == message) { // Avoid glitch and scrolling - UIImage *image = [[UIImage alloc] initWithCGImage:[asset thumbnail]]; - dispatch_async(dispatch_get_main_queue(), ^{ - [_messageImageView setImage:image]; - [_messageImageView setFullImageUrl:asset]; - [_messageImageView stopLoading]; - _messageImageView.hidden = NO; - }); - } - }); - } - failureBlock:^(NSError *error) { - LOGE(@"Can't read image"); - }]; - } - if (ftd.message != nil) { - _cancelButton.hidden = NO; - _fileTransferProgress.hidden = NO; - _downloadButton.hidden = YES; - } else { - _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES; - } - } else { - _messageText.hidden = YES; - _messageImageView.hidden = _cancelButton.hidden = _fileTransferProgress.hidden = (ftd.message == nil); - _downloadButton.hidden = !_cancelButton.hidden; + assert(is_external || localImage); + if (localImage) { + // we did not load the image yet, so start doing so + if (_messageImageView.image == nil) { + NSURL *imageUrl = [NSURL URLWithString:localImage]; + [_messageImageView startLoading]; + __block LinphoneChatMessage *achat = self.message; + [LinphoneManager.instance.photoLibrary assetForURL:imageUrl + resultBlock:^(ALAsset *asset) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), + ^(void) { + if (achat == self.message) { // Avoid glitch and scrolling + UIImage *image = [[UIImage alloc] initWithCGImage:[asset thumbnail]]; + dispatch_async(dispatch_get_main_queue(), ^{ + [_messageImageView setImage:image]; + [_messageImageView setFullImageUrl:asset]; + [_messageImageView stopLoading]; + _messageImageView.hidden = NO; + }); + } + }); + } + failureBlock:^(NSError *error) { + LOGE(@"Can't read image"); + }]; } - // simple text message - } else { - [_messageText setHidden:FALSE]; - /* We need to use an attributed string here so that data detector don't mess - * with the text style. See http://stackoverflow.com/a/20669356 */ - - NSAttributedString *attr_text = - [[NSAttributedString alloc] initWithString:self.textMessage - attributes:@{ - NSFontAttributeName : [UIFont systemFontOfSize:17.0], - NSForegroundColorAttributeName : [UIColor darkGrayColor] - }]; - _messageText.attributedText = attr_text; - _messageImageView.hidden = YES; - _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES; - } - - // Date - _dateLabel.text = - [LinphoneUtils timeToString:linphone_chat_message_get_time(message) withStyle:NSDateFormatterMediumStyle]; - - LinphoneChatMessageState state = linphone_chat_message_get_state(message); - BOOL outgoing = linphone_chat_message_is_outgoing(message); - - if (!outgoing) { - [_statusImage setAccessibilityValue:@"incoming"]; - _statusImage.hidden = TRUE; // not useful for incoming chats.. - } else if (state == LinphoneChatMessageStateInProgress) { - [_statusImage setImage:[UIImage imageNamed:@"chat_message_inprogress.png"]]; - [_statusImage setAccessibilityValue:@"in progress"]; - _statusImage.hidden = FALSE; - } else if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateFileTransferDone) { - [_statusImage setImage:[UIImage imageNamed:@"chat_message_delivered.png"]]; - [_statusImage setAccessibilityValue:@"delivered"]; - _statusImage.hidden = FALSE; - } else { - [_statusImage setImage:[UIImage imageNamed:@"chat_message_not_delivered.png"]]; - [_statusImage setAccessibilityValue:@"not delivered"]; - _statusImage.hidden = FALSE; - - NSAttributedString *resend_text = - [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Resend", @"Resend") - attributes:@{NSForegroundColorAttributeName : [UIColor redColor]}]; - [_dateLabel setAttributedText:resend_text]; - } - - if (outgoing) { - [_messageText setAccessibilityLabel:@"Outgoing message"]; - } else { - [_messageText setAccessibilityLabel:@"Incoming message"]; - } -} - -- (void)setEditing:(BOOL)editing { - [self setEditing:editing animated:FALSE]; -} - -- (void)setEditing:(BOOL)editing animated:(BOOL)animated { - if (animated) { - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:0.3]; - } - _deleteButton.hidden = !editing; - if (animated) { - [UIView commitAnimations]; - } -} - -#pragma mark - View Functions - -- (void)layoutSubviews { - [super layoutSubviews]; - if (message != nil) { - BOOL is_outgoing = linphone_chat_message_is_outgoing(message); - CGRect innerFrame; - innerFrame.size = [self.class viewSize:message width:[self frame].size.width]; - innerFrame.origin.y = 0.0f; - innerFrame.origin.x = is_outgoing ? self.frame.size.width - innerFrame.size.width : 0; - _innerView.frame = innerFrame; - - CGRect messageFrame = _bubbleView.frame; - messageFrame.origin.y = (_innerView.frame.size.height - messageFrame.size.height) / 2; - if (!is_outgoing) { - messageFrame.origin.y += 5; + // we are uploading the image + if (ftd.message != nil) { + _cancelButton.hidden = NO; + _fileTransferProgress.hidden = NO; + _downloadButton.hidden = YES; } else { - messageFrame.origin.y -= 5; + _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES; } - _backgroundImage.image = - is_outgoing ? [UIImage imageNamed:@"chat_bubble_outgoing"] : [UIImage imageNamed:@"chat_bubble_incoming"]; - _bubbleView.frame = messageFrame; - } -} - -#pragma mark - Action Functions - -- (IBAction)onDeleteClick:(id)event { - if (message != NULL) { - [ftd cancel]; - UITableView *tableView = VIEW(ChatConversationView).tableController.tableView; - NSIndexPath *indexPath = [tableView indexPathForCell:self]; - [tableView.dataSource tableView:tableView - commitEditingStyle:UITableViewCellEditingStyleDelete - forRowAtIndexPath:indexPath]; + // we must download the image: either it has already started (show cancel button) or not yet (show download + // button) + } else { + // CGRect newFrame = _imageSubView.frame; + // newFrame.origin.y = _messageImageView.frame.origin.y + ((ftd.message == nil) ? 0 : + //_messageImageView.frame.size.height); + // _imageSubView.frame = newFrame; + _messageImageView.hidden = _cancelButton.hidden = (ftd.message == nil); + _downloadButton.hidden = !_cancelButton.hidden; + _fileTransferProgress.hidden = NO; } } @@ -273,20 +150,25 @@ static UIFont *CELL_FONT = nil; [ftd cancel]; ftd = [[FileTransferDelegate alloc] init]; [self connectToFileDelegate:ftd]; - [ftd download:message]; + [ftd download:self.message]; _cancelButton.hidden = NO; _downloadButton.hidden = YES; } -- (IBAction)onCancelDownloadClick:(id)sender { +- (IBAction)onCancelClick:(id)sender { FileTransferDelegate *tmp = ftd; [self disconnectFromFileDelegate]; [tmp cancel]; + _fileTransferProgress.progress = 0; [self update]; } +- (void)onResendClick:(id)event { + [super onResendClick:event]; +} + - (IBAction)onImageClick:(id)event { - LinphoneChatMessageState state = linphone_chat_message_get_state(message); + LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); if (state == LinphoneChatMessageStateNotDelivered) { [self onResendClick:event]; } else { @@ -300,69 +182,6 @@ static UIFont *CELL_FONT = nil; } } -- (IBAction)onResendClick:(id)event { - if (message == nil) - return; - - LinphoneChatMessageState state = linphone_chat_message_get_state(message); - if (state == LinphoneChatMessageStateNotDelivered) { - if (linphone_chat_message_get_file_transfer_information(message) != NULL) { - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:message]; - NSURL *imageUrl = [NSURL URLWithString:localImage]; - - [self onDeleteClick:nil]; - - [[LinphoneManager instance] - .photoLibrary assetForURL:imageUrl - resultBlock:^(ALAsset *asset) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), - ^(void) { - UIImage *image = [[UIImage alloc] initWithCGImage:[asset thumbnail]]; - [_chatRoomDelegate startImageUpload:image url:imageUrl]; - }); - } - failureBlock:^(NSError *error) { - LOGE(@"Can't read image"); - }]; - } else { - [self onDeleteClick:nil]; - - 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) { - [_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil]; - }); - } - } -} -#pragma mark - State changed handling -static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState state) { - UIChatBubblePhotoCell *thiz = (__bridge UIChatBubblePhotoCell *)linphone_chat_message_get_user_data(msg); - LOGI(@"State for message [%p] changed to %s", msg, linphone_chat_message_state_to_string(state)); - if (linphone_chat_message_get_file_transfer_information(msg) != NULL) { - if (state == LinphoneChatMessageStateDelivered || state == LinphoneChatMessageStateNotDelivered) { - // we need to refresh the tableview because the filetransfer delegate unreffed - // the message message before state was LinphoneChatMessageStateFileTransferDone - - // if we are coming back from another view between unreffing and change of state, - // the transient message will not be found and it will not appear in the list of - // message, so we must refresh the table when we change to this state to ensure that - // all transient messages apppear - // ChatRoomViewController *controller = DYNAMIC_CAST( - // [PhoneMainView.instance changeCurrentView:ChatRoomViewController.compositeViewDescription - // push:TRUE], - // ChatRoomViewController); - // [controller.tableController setChatRoom:linphone_chat_message_get_chat_room(msg)]; - // This is breaking interface too much, it must be fixed in file transfer cb.. meanwhile, disabling it. - - if (thiz->ftd) { - [thiz->ftd stopAndDestroy]; - thiz->ftd = nil; - } - } - } - [thiz update]; -} - #pragma mark - LinphoneFileTransfer Notifications Handling - (void)connectToFileDelegate:(FileTransferDelegate *)aftd { @@ -389,9 +208,9 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st if (state == LinphoneChatMessageStateInProgress) { float progress = [[[notif userInfo] objectForKey:@"progress"] floatValue]; - // When uploading a file, the message file is first uploaded to the server, + // When uploading a file, the self.message file is first uploaded to the server, // so we are in progress state. Then state goes to filetransfertdone. Then, - // the exact same message is sent to the other participant and we come + // the exact same self.message is sent to the other participant and we come // back to in progress again. This second time is NOT an upload, so we must // not update progress! _fileTransferProgress.progress = MAX(_fileTransferProgress.progress, progress); @@ -410,6 +229,25 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st [self update]; } } -#endif + +- (CGSize)viewSizeWithWidth:(int)width { + 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 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.statusImage.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 c9b012581..85cb528f3 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.xib +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.xib @@ -1,102 +1,125 @@ - + - + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - + + + - - - - - - - - - - - - - - + @@ -110,9 +133,10 @@ - - - + + + + diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h index 1609e2d2d..c2f80616e 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.h +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -25,6 +25,7 @@ @interface UIChatBubbleTextCell : UITableViewCell +@property(readonly, nonatomic) LinphoneChatMessage *message; @property(nonatomic, weak) IBOutlet UIImageView *backgroundColorImage; @property(nonatomic, weak) IBOutlet UIRoundedImageView *avatarImage; @property(nonatomic, weak) IBOutlet UILabel *contactDateLabel; @@ -40,7 +41,9 @@ - (IBAction)onDeleteClick:(id)event; - (IBAction)onResendClick:(id)event; +- (void)update; + (NSString *)TextMessageForChat:(LinphoneChatMessage *)message; +- (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font; @end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 05b72399a..c0bdc8c4c 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -24,9 +24,7 @@ #import #import -@implementation UIChatBubbleTextCell { - LinphoneChatMessage *message; -} +@implementation UIChatBubbleTextCell #pragma mark - Lifecycle Functions @@ -50,60 +48,63 @@ #pragma mark - - (void)setChatMessage:(LinphoneChatMessage *)amessage { - if (amessage == message) { + if (amessage == _message) { return; } - if (message) { - linphone_chat_message_unref(message); - linphone_chat_message_set_user_data(message, NULL); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(message), NULL); + if (_message) { + linphone_chat_message_unref(_message); + linphone_chat_message_set_user_data(_message, NULL); + linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(_message), NULL); } - message = amessage; + _message = amessage; if (amessage) { - 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); + 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 *)textMessage { - return [self.class TextMessageForChat:message]; + return [self.class TextMessageForChat:_message]; } - (void)update { - if (message == nil) { - LOGW(@"Cannot update message room cell: null message"); + if (_message == nil) { + LOGW(@"Cannot update _message room cell: null _message"); return; } - [_messageText setHidden:FALSE]; - /* We need to use an attributed string here so that data detector don't mess - * with the text style. See http://stackoverflow.com/a/20669356 */ - NSAttributedString *attr_text = - [[NSAttributedString alloc] initWithString:self.textMessage - attributes:@{ - NSFontAttributeName : _messageText.font, - NSForegroundColorAttributeName : [UIColor darkGrayColor] - }]; - _messageText.attributedText = attr_text; + if (_messageText) { + [_messageText setHidden:FALSE]; + /* We need to use an attributed string here so that data detector don't mess + * with the text style. See http://stackoverflow.com/a/20669356 */ + + NSAttributedString *attr_text = + [[NSAttributedString alloc] initWithString:self.textMessage + attributes:@{ + NSFontAttributeName : _messageText.font, + NSForegroundColorAttributeName : [UIColor darkGrayColor] + }]; + _messageText.attributedText = attr_text; + } // Date _contactDateLabel.text = [NSString - stringWithFormat:@"%@ - %@", [LinphoneUtils timeToString:linphone_chat_message_get_time(message) + stringWithFormat:@"%@ - %@", [LinphoneUtils timeToString:linphone_chat_message_get_time(_message) withStyle:NSDateFormatterShortStyle], - [FastAddressBook displayNameForAddress:linphone_chat_message_get_peer_address(message)]]; + [FastAddressBook displayNameForAddress:linphone_chat_message_get_peer_address(_message)]]; - LinphoneChatMessageState state = linphone_chat_message_get_state(message); - BOOL outgoing = linphone_chat_message_is_outgoing(message); + LinphoneChatMessageState state = linphone_chat_message_get_state(_message); + BOOL outgoing = linphone_chat_message_is_outgoing(_message); _backgroundColorImage.image = _bottomBarColor.image = [UIImage imageNamed:(outgoing ? @"color_A" : @"color_D")]; if (!outgoing) { @@ -129,9 +130,9 @@ } if (outgoing) { - [_messageText setAccessibilityLabel:@"Outgoing message"]; + [_messageText setAccessibilityLabel:@"Outgoing _message"]; } else { - [_messageText setAccessibilityLabel:@"Incoming message"]; + [_messageText setAccessibilityLabel:@"Incoming _message"]; } } @@ -146,7 +147,7 @@ #pragma mark - Action Functions - (IBAction)onDeleteClick:(id)event { - if (message != NULL) { + if (_message != NULL) { UITableView *tableView = VIEW(ChatConversationView).tableController.tableView; NSIndexPath *indexPath = [tableView indexPathForCell:self]; [tableView.dataSource tableView:tableView @@ -156,13 +157,13 @@ } - (IBAction)onResendClick:(id)event { - if (message == nil) + if (_message == nil) return; - LinphoneChatMessageState state = linphone_chat_message_get_state(message); + LinphoneChatMessageState state = linphone_chat_message_get_state(_message); if (state == LinphoneChatMessageStateNotDelivered) { - if (linphone_chat_message_get_file_transfer_information(message) != NULL) { - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:message]; + if (linphone_chat_message_get_file_transfer_information(_message) != NULL) { + NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message]; NSURL *imageUrl = [NSURL URLWithString:localImage]; [self onDeleteClick:nil]; @@ -199,56 +200,46 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st #pragma mark - Bubble size computing +- (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 7) { + return [text boundingRectWithSize:size + options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) + attributes:@{ + NSFontAttributeName : font + } + context:nil] + .size; + } +#endif + { return [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping]; } +} + - (CGSize)viewSizeWithWidth:(int)width { static const CGFloat TEXT_MIN_HEIGHT = 32.; 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 IMAGE_HEIGHT = 100.0f; // TODO: move that in bubblephoto + static const CGFloat IMAGE_HEIGHT = 100.0f; static const CGFloat IMAGE_WIDTH = 100.0f; static const CGFloat CHECK_BOX_WIDTH = 40; - CGSize messageSize, dateSize; int messageAvailableWidth = width - MARGIN_WIDTH - CHECK_BOX_WIDTH; - const char *url = linphone_chat_message_get_external_body_url(message); - if (url == nil && linphone_chat_message_get_file_transfer_information(message) == NULL) { - NSString *text = [UIChatBubbleTextCell TextMessageForChat:message]; + const char *url = linphone_chat_message_get_external_body_url(_message); + NSString *text = [UIChatBubbleTextCell TextMessageForChat:_message]; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 - if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 7) { - messageSize = - [text boundingRectWithSize:CGSizeMake(messageAvailableWidth, CGFLOAT_MAX) - options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) - attributes:@{ - NSFontAttributeName : _messageText.font - } - context:nil] - .size; - dateSize = [_contactDateLabel.text boundingRectWithSize:_contactDateLabel.frame.size - options:(NSStringDrawingUsesLineFragmentOrigin | - - NSStringDrawingUsesFontLeading) - attributes:@{ - NSFontAttributeName : _contactDateLabel.font - } - context:nil] - .size; - } else -#endif - { - messageSize = [text sizeWithFont:_messageText.font - constrainedToSize:CGSizeMake(messageAvailableWidth, CGFLOAT_MAX) - lineBreakMode:NSLineBreakByCharWrapping]; - dateSize = [_contactDateLabel.text sizeWithFont:_contactDateLabel.font - constrainedToSize:_contactDateLabel.frame.size - lineBreakMode:NSLineBreakByCharWrapping]; - } - } else { + CGSize messageSize; + if (url != nil || linphone_chat_message_get_file_transfer_information(_message) != NULL) { messageSize = CGSizeMake(IMAGE_WIDTH, IMAGE_HEIGHT); + } else { + 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)); } - 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]; CGSize bubbleSize; bubbleSize.width = MAX(messageSize.width, dateSize.width + _statusImage.frame.size.width + 5) + MARGIN_WIDTH; @@ -259,9 +250,9 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st - (void)layoutSubviews { [super layoutSubviews]; - if (message != nil) { + if (_message != nil) { UITableView *tableView = VIEW(ChatConversationView).tableController.tableView; - BOOL is_outgoing = linphone_chat_message_is_outgoing(message); + BOOL is_outgoing = linphone_chat_message_is_outgoing(_message); CGRect bubbleFrame = _bubbleView.frame; bubbleFrame.size = [self viewSizeWithWidth:self.frame.size.width]; bubbleFrame.origin.x = diff --git a/Classes/LinphoneUI/UIConfirmationDialog.xib b/Classes/LinphoneUI/UIConfirmationDialog.xib index eaeccaa3a..da599a181 100644 --- a/Classes/LinphoneUI/UIConfirmationDialog.xib +++ b/Classes/LinphoneUI/UIConfirmationDialog.xib @@ -1,7 +1,6 @@ - + - diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index b1be7535d..e4d8f1512 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -124,6 +124,7 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m // this is the last time we will be notified, so destroy ourselve if (remaining <= size) { LOGI(@"Upload ended"); + linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(thiz.message), NULL); thiz.message = NULL; [thiz stopAndDestroy]; } diff --git a/submodules/belle-sip b/submodules/belle-sip index 0efd4dfd5..c9e603882 160000 --- a/submodules/belle-sip +++ b/submodules/belle-sip @@ -1 +1 @@ -Subproject commit 0efd4dfd5b37fb28ba1ecbabcc8d37defddfd517 +Subproject commit c9e603882c3b6bd2b387f17ba8c003ffb4f218fe diff --git a/submodules/linphone b/submodules/linphone index 4e3ea86c9..34d3a0583 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 4e3ea86c90cf125f1348dc4f296540d1696e5851 +Subproject commit 34d3a0583b69946d35b4c7e968530e359423603f