diff --git a/Classes/ChatConversationTableView.h b/Classes/ChatConversationTableView.h index a0f5b3f19..54c7a4731 100644 --- a/Classes/ChatConversationTableView.h +++ b/Classes/ChatConversationTableView.h @@ -41,4 +41,6 @@ - (void)updateChatEntry:(LinphoneChatMessage *)chat; - (void)setChatRoom:(LinphoneChatRoom *)room; ++ (CGSize)viewSize:(LinphoneChatMessage *)message width:(int)width; + @end diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index 46d059313..2844ec625 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -19,9 +19,19 @@ #import "LinphoneManager.h" #import "ChatConversationTableView.h" -#import "UIChatBubbleCell.h" +#import "UIChatBubbleTextCell.h" +#import "UIChatBubblePhotoCell.h" #import "PhoneMainView.h" +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; + @implementation ChatConversationTableView @synthesize chatRoomDelegate; @@ -167,13 +177,18 @@ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *kCellId = @"UIChatRoomCell"; - UIChatBubbleCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; - if (cell == nil) { - cell = [[UIChatBubbleCell alloc] initWithIdentifier:kCellId]; - } - + NSString *kCellId = nil; LinphoneChatMessage *chat = ms_list_nth_data(self->messageList, (int)[indexPath row]); + if (linphone_chat_message_get_file_transfer_information(chat) || + linphone_chat_message_get_external_body_url(chat)) { + kCellId = NSStringFromClass(UIChatBubblePhotoCell.class); + } else { + kCellId = NSStringFromClass(UIChatBubbleTextCell.class); + } + UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; + if (cell == nil) { + cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; + } [cell setChatMessage:chat]; [cell setChatRoomDelegate:chatRoomDelegate]; return cell; @@ -209,7 +224,46 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { LinphoneChatMessage *message = ms_list_nth_data(self->messageList, (int)[indexPath row]); - return [UIChatBubbleCell height:message width:[self.view frame].size.width]; + return [self.class viewSize:message width:[self.view frame].size.width].height; +} + +#pragma mark - Cell dimension + ++ (CGSize)viewSize:(LinphoneChatMessage *)message width:(int)width { + CGSize messageSize; + 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]; + if (CELL_FONT == nil) { + CELL_FONT = [UIFont systemFontOfSize:CELL_FONT_SIZE]; + } +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if (UIDevice.currentDevice.systemVersion.doubleValue >= 7) { + messageSize = + [text boundingRectWithSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, CGFLOAT_MAX) + options:(NSStringDrawingUsesLineFragmentOrigin | + NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading) + attributes:@{ + NSFontAttributeName : CELL_FONT + } context:nil] + .size; + } else +#endif + { + messageSize = [text sizeWithFont:CELL_FONT + constrainedToSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, 10000.0f) + lineBreakMode:NSLineBreakByTruncatingTail]; + } + } else { + messageSize = CGSizeMake(CELL_IMAGE_WIDTH, CELL_IMAGE_HEIGHT); + } + messageSize.height += CELL_MESSAGE_Y_MARGIN; + if (messageSize.height < CELL_MIN_HEIGHT) + messageSize.height = CELL_MIN_HEIGHT; + messageSize.width += CELL_MESSAGE_X_MARGIN; + if (messageSize.width < CELL_MIN_WIDTH) + messageSize.width = CELL_MIN_WIDTH; + return messageSize; } @end diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index a27ccf241..b20ac1f1e 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -21,7 +21,7 @@ #import "PhoneMainView.h" #import "Utils.h" #import "FileTransferDelegate.h" -#import "UIChatBubbleCell.h" +#import "UIChatBubbleTextCell.h" @implementation ChatConversationView diff --git a/Classes/LinphoneUI/Base.lproj/UIChatBubbleCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatBubbleCell.xib deleted file mode 100644 index ddda4a94f..000000000 --- a/Classes/LinphoneUI/Base.lproj/UIChatBubbleCell.xib +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/LinphoneUI/UIChatBubbleCell.h b/Classes/LinphoneUI/UIChatBubbleCell.h deleted file mode 100644 index 92f6fc128..000000000 --- a/Classes/LinphoneUI/UIChatBubbleCell.h +++ /dev/null @@ -1,61 +0,0 @@ -/* UIChatRoomCell.h - * - * Copyright (C) 2012 Belledonne Comunications, Grenoble, France - * - * 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 2 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 Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#import - -#import "ChatConversationTableView.h" -#import "UILoadingImageView.h" -#import "UITransparentTVCell.h" -#import "UITextViewNoDefine.h" -#include "linphone/linphonecore.h" -#import "FileTransferDelegate.h" - -@interface UIChatBubbleCell : UITransparentTVCell { - LinphoneChatMessage *chat; -} - -@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(nonatomic, strong) IBOutlet UITapGestureRecognizer *imageTapGestureRecognizer; -@property(nonatomic, strong) IBOutlet UITapGestureRecognizer *resendTapGestureRecognizer; -@property(weak, nonatomic) IBOutlet UIProgressView *fileTransferProgress; -@property(weak, nonatomic) IBOutlet UIButton *cancelButton; - -- (id)initWithIdentifier:(NSString *)identifier; -+ (CGFloat)height:(LinphoneChatMessage *)chatMessage width:(int)width; - -@property(nonatomic, strong) id chatRoomDelegate; - -- (IBAction)onDeleteClick:(id)event; -- (IBAction)onDownloadClick:(id)event; -- (IBAction)onImageClick:(id)event; -- (IBAction)onCancelDownloadClick:(id)sender; - -- (void)setChatMessage:(LinphoneChatMessage *)message; - -- (void)connectToFileDelegate:(FileTransferDelegate *)ftd; - -@end diff --git a/Classes/LinphoneUI/UIChatBubbleCell.m b/Classes/LinphoneUI/UIChatBubbleCell.m deleted file mode 100644 index e166b7f25..000000000 --- a/Classes/LinphoneUI/UIChatBubbleCell.m +++ /dev/null @@ -1,517 +0,0 @@ -/* UIChatRoomCell.m - * - * Copyright (C) 2012 Belledonne Comunications, Grenoble, France - * - * 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 2 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 Library General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#import "UIChatBubbleCell.h" -#import "Utils.h" -#import "LinphoneManager.h" -#import "PhoneMainView.h" - -#import -#import -#include "linphone/linphonecore.h" - -@implementation UIChatBubbleCell { - FileTransferDelegate *ftd; -} - -@synthesize innerView; -@synthesize bubbleView; -@synthesize backgroundImage; -@synthesize messageImageView; -@synthesize messageText; -@synthesize deleteButton; -@synthesize dateLabel; -@synthesize statusImage; -@synthesize downloadButton; -@synthesize chatRoomDelegate; -@synthesize imageTapGestureRecognizer; -@synthesize resendTapGestureRecognizer; - -static const CGFloat CELL_MIN_HEIGHT = 50.0f; -static const CGFloat CELL_MIN_WIDTH = 150.0f; -// static const CGFloat CELL_MAX_WIDTH = 320.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]; - imageTapGestureRecognizer = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onImageClick:)]; - [messageImageView addGestureRecognizer:imageTapGestureRecognizer]; - - resendTapGestureRecognizer = - [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onResendClick:)]; - [innerView addGestureRecognizer:resendTapGestureRecognizer]; - - [self addSubview:innerView]; - [deleteButton setAlpha:0.0f]; - - // 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 setFrame:messageCoords]; - messageText.allowSelectAll = TRUE; - } - - return self; -} - -- (void)dealloc { - [self disconnectFromFileDelegate]; - if (self->chat) { - linphone_chat_message_unref(self->chat); - linphone_chat_message_set_user_data(self->chat, NULL); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), NULL); - self->chat = NULL; - } -} - -#pragma mark - - -- (void)setChatMessage:(LinphoneChatMessage *)message { - if (message != self->chat) { - if (self->chat) { - linphone_chat_message_unref(self->chat); - linphone_chat_message_set_user_data(self->chat, NULL); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), NULL); - } - self->chat = message; - messageImageView.image = nil; - [self disconnectFromFileDelegate]; - if (self->chat) { - linphone_chat_message_ref(self->chat); - linphone_chat_message_set_user_data(self->chat, (void *)CFBridgingRetain(self)); - linphone_chat_message_cbs_set_msg_state_changed(linphone_chat_message_get_callbacks(self->chat), - message_status); - - const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(message); - 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); - [self connectToFileDelegate:aftd]; - break; - } - } - } - } - [self update]; - } -} - -+ (NSString *)decodeTextMessage:(const char *)text { - NSString *decoded = [NSString stringWithUTF8String:text]; - if (decoded == nil) { - // couldn't decode the string as UTF8, do a lossy conversion - decoded = [NSString stringWithCString:text encoding:NSASCIIStringEncoding]; - if (decoded == nil) { - decoded = @"(invalid string)"; - } - } - return decoded; -} - -- (void)update { - if (chat == nil) { - LOGW(@"Cannot update chat room cell: null chat"); - return; - } - const char *url = linphone_chat_message_get_external_body_url(chat); - const char *text = linphone_chat_message_get_text(chat); - BOOL is_external = - (url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(chat); - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat]; - - // 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 = chat; - [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->chat) { // 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; - } - // simple text message - } else { - [messageText setHidden:FALSE]; - if (text) { - NSString *nstext = [UIChatBubbleCell decodeTextMessage:text]; - - /* 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:nstext - attributes:@{ - NSFontAttributeName : [UIFont systemFontOfSize:17.0], - NSForegroundColorAttributeName : [UIColor darkGrayColor] - }]; - messageText.attributedText = attr_text; - - } else { - messageText.text = @""; - } - messageImageView.hidden = YES; - _cancelButton.hidden = _fileTransferProgress.hidden = downloadButton.hidden = YES; - } - - // Date - dateLabel.text = - [LinphoneUtils timeToString:linphone_chat_message_get_time(chat) withStyle:NSDateFormatterMediumStyle]; - - LinphoneChatMessageState state = linphone_chat_message_get_state(chat); - BOOL outgoing = linphone_chat_message_is_outgoing(chat); - - 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]; - } - if (editing) { - [deleteButton setAlpha:1.0f]; - } else { - [deleteButton setAlpha:0.0f]; - } - if (animated) { - [UIView commitAnimations]; - } -} - -+ (CGSize)viewSize:(LinphoneChatMessage *)chat width:(int)width { - CGSize messageSize; - const char *url = linphone_chat_message_get_external_body_url(chat); - const char *text = linphone_chat_message_get_text(chat); - NSString *messageText = text ? [UIChatBubbleCell decodeTextMessage:text] : @""; - if (url == nil && linphone_chat_message_get_file_transfer_information(chat) == NULL) { - if (CELL_FONT == nil) { - CELL_FONT = [UIFont systemFontOfSize:CELL_FONT_SIZE]; - } - -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 - - if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 7) { - messageSize = [messageText boundingRectWithSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, CGFLOAT_MAX) - options:(NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingTruncatesLastVisibleLine | - NSStringDrawingUsesFontLeading) - attributes:@{ - NSFontAttributeName : CELL_FONT - } context:nil] - .size; - } else -#endif - { - messageSize = [messageText sizeWithFont:CELL_FONT - constrainedToSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, 10000.0f) - lineBreakMode:NSLineBreakByTruncatingTail]; - } - } else { - messageSize = CGSizeMake(CELL_IMAGE_WIDTH, CELL_IMAGE_HEIGHT); - } - messageSize.height += CELL_MESSAGE_Y_MARGIN; - if (messageSize.height < CELL_MIN_HEIGHT) - messageSize.height = CELL_MIN_HEIGHT; - messageSize.width += CELL_MESSAGE_X_MARGIN; - if (messageSize.width < CELL_MIN_WIDTH) - messageSize.width = CELL_MIN_WIDTH; - return messageSize; -} - -+ (CGFloat)height:(LinphoneChatMessage *)chatMessage width:(int)width { - return [UIChatBubbleCell viewSize:chatMessage width:width].height; -} - -#pragma mark - View Functions - -- (void)layoutSubviews { - [super layoutSubviews]; - if (chat != nil) { - // Resize inner - CGRect innerFrame; - BOOL is_outgoing = linphone_chat_message_is_outgoing(chat); - innerFrame.size = [UIChatBubbleCell viewSize:chat width:[self frame].size.width]; - if (!is_outgoing) { // Inverted - innerFrame.origin.x = 0.0f; - innerFrame.origin.y = 0.0f; - } else { - innerFrame.origin.x = [self frame].size.width - innerFrame.size.width; - innerFrame.origin.y = 0.0f; - } - [innerView setFrame:innerFrame]; - - CGRect messageFrame = [bubbleView frame]; - messageFrame.origin.y = ([innerView frame].size.height - messageFrame.size.height) / 2; - if (!is_outgoing) { // Inverted - UIImage *image = [UIImage imageNamed:@"chat_bubble_incoming"]; - image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(26, 32, 34, 56)]; - [backgroundImage setImage:image]; - messageFrame.origin.y += 5; - } else { - UIImage *image = [UIImage imageNamed:@"chat_bubble_outgoing"]; - image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(14, 15, 25, 40)]; - [backgroundImage setImage:image]; - messageFrame.origin.y -= 5; - } - [bubbleView setFrame:messageFrame]; - } -} - -#pragma mark - Action Functions - -- (IBAction)onDeleteClick:(id)event { - if (chat != NULL) { - if (ftd.message != nil) { - [ftd cancel]; - } - UIView *view = [self superview]; - // Find TableViewCell - while (view != nil && ![view isKindOfClass:[UITableView class]]) - view = [view superview]; - if (view != nil) { - UITableView *tableView = (UITableView *)view; - NSIndexPath *indexPath = [tableView indexPathForCell:self]; - [[tableView dataSource] tableView:tableView - commitEditingStyle:UITableViewCellEditingStyleDelete - forRowAtIndexPath:indexPath]; - } - } -} - -- (IBAction)onDownloadClick:(id)event { - if (ftd.message == nil) { - ftd = [[FileTransferDelegate alloc] init]; - [self connectToFileDelegate:ftd]; - [ftd download:chat]; - _cancelButton.hidden = NO; - downloadButton.hidden = YES; - } -} - -- (IBAction)onCancelDownloadClick:(id)sender { - FileTransferDelegate *tmp = ftd; - [self disconnectFromFileDelegate]; - [tmp cancel]; - [self update]; -} - -- (IBAction)onImageClick:(id)event { - LinphoneChatMessageState state = linphone_chat_message_get_state(self->chat); - if (state == LinphoneChatMessageStateNotDelivered) { - [self onResendClick:nil]; - } else { - if (![messageImageView isLoading]) { - ImageView *view = VIEW(ImageView); - [PhoneMainView.instance changeCurrentView:view.compositeViewDescription push:TRUE]; - CGImageRef fullScreenRef = [[messageImageView.fullImageUrl defaultRepresentation] fullScreenImage]; - UIImage *fullScreen = [UIImage imageWithCGImage:fullScreenRef]; - [view setImage:fullScreen]; - } - } -} - -- (IBAction)onResendClick:(id)event { - if (chat == nil) - return; - - LinphoneChatMessageState state = linphone_chat_message_get_state(self->chat); - if (state == LinphoneChatMessageStateNotDelivered) { - if (linphone_chat_message_get_file_transfer_information(chat) != NULL) { - NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:chat]; - 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 { - const char *text = linphone_chat_message_get_text(self->chat); - NSString *message = text ? [NSString stringWithUTF8String:text] : nil; - - [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:message withExternalUrl:nil]; - }); - } - } -} -#pragma mark - State changed handling -static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState state) { - UIChatBubbleCell *thiz = (__bridge UIChatBubbleCell *)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 chat 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 { - ftd = aftd; - _fileTransferProgress.progress = 0; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onFileTransferSendUpdate:) - name:kLinphoneFileTransferSendUpdate - object:ftd]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onFileTransferRecvUpdate:) - name:kLinphoneFileTransferRecvUpdate - object:ftd]; -} - -- (void)disconnectFromFileDelegate { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - ftd = nil; -} - -- (void)onFileTransferSendUpdate:(NSNotification *)notif { - LinphoneChatMessageState state = [[[notif userInfo] objectForKey:@"state"] intValue]; - - if (state == LinphoneChatMessageStateInProgress) { - float progress = [[[notif userInfo] objectForKey:@"progress"] floatValue]; - // When uploading a file, the 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 chat 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); - _fileTransferProgress.hidden = _cancelButton.hidden = (_fileTransferProgress.progress == 1.f); - } else { - [self update]; - } -} -- (void)onFileTransferRecvUpdate:(NSNotification *)notif { - LinphoneChatMessageState state = [[[notif userInfo] objectForKey:@"state"] intValue]; - if (state == LinphoneChatMessageStateInProgress) { - float progress = [[[notif userInfo] objectForKey:@"progress"] floatValue]; - _fileTransferProgress.progress = MAX(_fileTransferProgress.progress, progress); - _fileTransferProgress.hidden = _cancelButton.hidden = (_fileTransferProgress.progress == 1.f); - } else { - [self update]; - } -} - -@end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index 2fae8fe35..b6915c89f 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -27,28 +27,28 @@ @interface UIChatBubblePhotoCell : UITransparentTVCell -@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 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; @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 820a47b0c..cae51760a 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -28,6 +28,8 @@ LinphoneChatMessage *message; FileTransferDelegate *ftd; } +#if 0 + static const CGFloat CELL_MIN_HEIGHT = 50.0f; static const CGFloat CELL_MIN_WIDTH = 150.0f; @@ -229,47 +231,6 @@ static UIFont *CELL_FONT = nil; } } -+ (CGSize)viewSize:(LinphoneChatMessage *)message width:(int)width { - CGSize messageSize; - 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 = [self.class TextMessageForChat:message]; - if (CELL_FONT == nil) { - CELL_FONT = [UIFont systemFontOfSize:CELL_FONT_SIZE]; - } -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 - if (UIDevice.currentDevice.systemVersion.doubleValue >= 7) { - messageSize = - [text boundingRectWithSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, CGFLOAT_MAX) - options:(NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading) - attributes:@{ - NSFontAttributeName : CELL_FONT - } context:nil] - .size; - } else -#endif - { - messageSize = [text sizeWithFont:CELL_FONT - constrainedToSize:CGSizeMake(width - CELL_MESSAGE_X_MARGIN, 10000.0f) - lineBreakMode:NSLineBreakByTruncatingTail]; - } - } else { - messageSize = CGSizeMake(CELL_IMAGE_WIDTH, CELL_IMAGE_HEIGHT); - } - messageSize.height += CELL_MESSAGE_Y_MARGIN; - if (messageSize.height < CELL_MIN_HEIGHT) - messageSize.height = CELL_MIN_HEIGHT; - messageSize.width += CELL_MESSAGE_X_MARGIN; - if (messageSize.width < CELL_MIN_WIDTH) - messageSize.width = CELL_MIN_WIDTH; - return messageSize; -} - -+ (CGFloat)height:(LinphoneChatMessage *)chatMessage width:(int)width { - return [self.class viewSize:chatMessage width:width].height; -} - #pragma mark - View Functions - (void)layoutSubviews { @@ -449,5 +410,6 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st [self update]; } } +#endif @end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h new file mode 100644 index 000000000..d3ca14c16 --- /dev/null +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -0,0 +1,47 @@ +/* UIChatRoomCell.h + * + * Copyright (C) 2012 Belledonne Comunications, Grenoble, France + * + * 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 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#import + +#import "UITransparentTVCell.h" +#import "UITextViewNoDefine.h" +#import "ChatConversationTableView.h" +#import "UIRoundedImageView.h" + +@interface UIChatBubbleTextCell : UITransparentTVCell + +@property(nonatomic, weak) IBOutlet UIView *innerView; +@property(nonatomic, weak) IBOutlet UIView *bubbleView; +@property(nonatomic, weak) IBOutlet UIImageView *backgroundColor; +@property(nonatomic, weak) IBOutlet UIRoundedImageView *avatarImage; +@property(nonatomic, weak) IBOutlet UILabel *contactDateLabel; +@property(nonatomic, weak) IBOutlet UIImageView *statusImage; +@property(nonatomic, weak) IBOutlet UITextViewNoDefine *messageText; +@property(nonatomic, weak) IBOutlet UIButton *deleteButton; +@property(weak, nonatomic) IBOutlet UIImageView *bottomBarColor; +@property(nonatomic, strong) id chatRoomDelegate; + +- (void)setChatMessage:(LinphoneChatMessage *)message; + +- (IBAction)onDeleteClick:(id)event; +- (IBAction)onResendClick:(id)event; + ++ (NSString *)TextMessageForChat:(LinphoneChatMessage *)message; + +@end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m new file mode 100644 index 000000000..17c00b952 --- /dev/null +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -0,0 +1,235 @@ +/* UIChatRoomCell.m + * + * Copyright (C) 2012 Belledonne Comunications, Grenoble, France + * + * 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 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#import "UIChatBubbleTextCell.h" +#import "LinphoneManager.h" +#import "PhoneMainView.h" + +#import +#import + +@implementation UIChatBubbleTextCell { + LinphoneChatMessage *message; +} + +#pragma mark - Lifecycle Functions + +- (id)initWithIdentifier:(NSString *)identifier { + if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) { + [[NSBundle mainBundle] loadNibNamed:@"UIChatBubbleTextCell" 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 + } + + return self; +} + +- (void)dealloc { + [self setChatMessage:NULL]; +} + +#pragma mark - + +- (void)setChatMessage:(LinphoneChatMessage *)amessage { + 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); + } + + 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); + [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]; +} + +- (void)update { + 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 : [UIFont systemFontOfSize:17.0], + NSForegroundColorAttributeName : [UIColor darkGrayColor] + }]; + _messageText.attributedText = attr_text; + + // Date + _contactDateLabel.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.accessibilityValue = @"incoming"; + _statusImage.hidden = TRUE; // not useful for incoming chats.. + } else if (state == LinphoneChatMessageStateInProgress) { + _statusImage.image = [UIImage imageNamed:@"chat_message_inprogress.png"]; + _statusImage.accessibilityValue = @"in progress"; + _statusImage.hidden = FALSE; + } else if (state == LinphoneChatMessageStateDelivered) { + _statusImage.image = [UIImage imageNamed:@"chat_message_delivered.png"]; + _statusImage.accessibilityValue = @"delivered"; + _statusImage.hidden = FALSE; + } else { + _statusImage.image = [UIImage imageNamed:@"chat_message_not_delivered.png"]; + _statusImage.accessibilityValue = @"not delivered"; + _statusImage.hidden = FALSE; + + NSAttributedString *resend_text = + [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Resend", @"Resend") + attributes:@{NSForegroundColorAttributeName : [UIColor redColor]}]; + [_contactDateLabel 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 = [ChatConversationTableView 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; + } else { + messageFrame.origin.y -= 5; + } + _backgroundColor.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) { + UITableView *tableView = VIEW(ChatConversationView).tableController.tableView; + NSIndexPath *indexPath = [tableView indexPathForCell:self]; + [tableView.dataSource tableView:tableView + commitEditingStyle:UITableViewCellEditingStyleDelete + forRowAtIndexPath:indexPath]; + } +} + +- (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) { + UIChatBubbleTextCell *thiz = (__bridge UIChatBubbleTextCell *)linphone_chat_message_get_user_data(msg); + LOGI(@"State for message [%p] changed to %s", msg, linphone_chat_message_state_to_string(state)); + [thiz update]; +} + +@end diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.xib b/Classes/LinphoneUI/UIChatBubbleTextCell.xib new file mode 100644 index 000000000..ee8ddbd2c --- /dev/null +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.xib @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Classes/LinphoneUI/ar.lproj/UIChatBubbleCell.strings b/Classes/LinphoneUI/ar.lproj/UIChatBubbleCell.strings deleted file mode 100644 index 23bd6fc18..000000000 Binary files a/Classes/LinphoneUI/ar.lproj/UIChatBubbleCell.strings and /dev/null differ diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 2631164a5..b9d5a90c1 100755 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -247,7 +247,6 @@ 639CEAFD1A1DF4D9004DE38F /* StatusBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEAFF1A1DF4D9004DE38F /* StatusBarView.xib */; }; 639CEB001A1DF4E4004DE38F /* UIHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB021A1DF4E4004DE38F /* UIHistoryCell.xib */; }; 639CEB031A1DF4EB004DE38F /* UICompositeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB051A1DF4EB004DE38F /* UICompositeView.xib */; }; - 639CEB061A1DF4F1004DE38F /* UIChatBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB081A1DF4F1004DE38F /* UIChatBubbleCell.xib */; }; 639CEB091A1DF4FA004DE38F /* UIChatCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */; }; 63AADBE81B6A0FF200AA16FD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 63AADBC41B6A0FF200AA16FD /* Localizable.strings */; }; 63AADBE91B6A0FF200AA16FD /* hold.wav in Resources */ = {isa = PBXBuildFile; fileRef = 63AADBC91B6A0FF200AA16FD /* hold.wav */; }; @@ -280,6 +279,7 @@ 63B81A0E1B57DA33009604A6 /* TPKeyboardAvoidingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B81A071B57DA33009604A6 /* TPKeyboardAvoidingScrollView.m */; }; 63B81A0F1B57DA33009604A6 /* TPKeyboardAvoidingTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B81A091B57DA33009604A6 /* TPKeyboardAvoidingTableView.m */; }; 63B81A101B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B81A0B1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m */; }; + 63BC49D41BA1AA6F004EC273 /* UIChatBubbleTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63BC49D31BA1AA6F004EC273 /* UIChatBubbleTextCell.xib */; }; 63CD4B4F1A5AAC8C00B84282 /* DTAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CD4B4E1A5AAC8C00B84282 /* DTAlertView.m */; }; 63CFEDE81B9EDD74007EA5BD /* libantlr3c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 223CA7E416D9255800EF1BEC /* libantlr3c.a */; }; 63CFEDE91B9EDD74007EA5BD /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22276E8013C73D3100210156 /* libavcodec.a */; }; @@ -384,7 +384,7 @@ D381881115FE3F0B00C3EDCA /* UICallCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D381881415FE3F0B00C3EDCA /* UICallCell.xib */; }; D381881915FE3FCA00C3EDCA /* CallView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D381881C15FE3FCA00C3EDCA /* CallView.xib */; }; D3A55FBC15877E5E003FD403 /* UIContactCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A55FBB15877E5E003FD403 /* UIContactCell.m */; }; - D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleCell.m */; }; + D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */; }; D3C6526715AC1A8F0092A874 /* UIEditableTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3C6526615AC1A8F0092A874 /* UIEditableTableViewCell.m */; }; D3D5126C160B3A8E00946DF8 /* AssistantSubviews.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3D5126A160B3A8E00946DF8 /* AssistantSubviews.xib */; }; D3EA53FD159850E80037DC6B /* LinphoneManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D3EA53FC159850E80037DC6B /* LinphoneManager.m */; }; @@ -850,7 +850,6 @@ 639CEAFE1A1DF4D9004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/StatusBarView.xib; sourceTree = ""; }; 639CEB011A1DF4E4004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIHistoryCell.xib; sourceTree = ""; }; 639CEB041A1DF4EB004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UICompositeView.xib; sourceTree = ""; }; - 639CEB071A1DF4F1004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatBubbleCell.xib; sourceTree = ""; }; 639CEB0A1A1DF4FA004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatCell.xib; sourceTree = ""; }; 639CEB0C1A1DF528004DE38F /* fr */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UICallCell.strings; sourceTree = ""; }; 639CEB0D1A1DF52C004DE38F /* ru */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/UICallCell.strings; sourceTree = ""; }; @@ -896,6 +895,7 @@ 63B81A091B57DA33009604A6 /* TPKeyboardAvoidingTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyboardAvoidingTableView.m; sourceTree = ""; }; 63B81A0A1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+TPKeyboardAvoidingAdditions.h"; sourceTree = ""; }; 63B81A0B1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+TPKeyboardAvoidingAdditions.m"; sourceTree = ""; }; + 63BC49D31BA1AA6F004EC273 /* UIChatBubbleTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UIChatBubbleTextCell.xib; sourceTree = ""; }; 63CD4B4D1A5AAC8C00B84282 /* DTAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTAlertView.h; sourceTree = ""; }; 63CD4B4E1A5AAC8C00B84282 /* DTAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTAlertView.m; sourceTree = ""; }; 63CFEDE21B9EDD36007EA5BD /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = "liblinphone-sdk/apple-darwin/lib/libswresample.a"; sourceTree = ""; }; @@ -1013,8 +1013,8 @@ D380801215C299D0005BE9BC /* ColorSpaceUtilites.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ColorSpaceUtilites.m; path = Utils/ColorSpaceUtilites.m; sourceTree = ""; }; D3A55FBA15877E5E003FD403 /* UIContactCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIContactCell.h; sourceTree = ""; }; D3A55FBB15877E5E003FD403 /* UIContactCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIContactCell.m; sourceTree = ""; }; - D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatBubbleCell.h; sourceTree = ""; }; - D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatBubbleCell.m; sourceTree = ""; }; + D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatBubbleTextCell.h; sourceTree = ""; }; + D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatBubbleTextCell.m; sourceTree = ""; }; D3C6526515AC1A8F0092A874 /* UIEditableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIEditableTableViewCell.h; sourceTree = ""; }; D3C6526615AC1A8F0092A874 /* UIEditableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIEditableTableViewCell.m; sourceTree = ""; }; D3EA53FB159850E80037DC6B /* LinphoneManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinphoneManager.h; sourceTree = ""; }; @@ -1118,7 +1118,6 @@ F0AF06FD1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/CallIncomingView.strings; sourceTree = ""; }; F0AF07011A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UICallCell.strings; sourceTree = ""; }; F0AF07021A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIChatCell.strings; sourceTree = ""; }; - F0AF07031A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIChatBubbleCell.strings; sourceTree = ""; }; F0AF07041A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UICompositeView.strings; sourceTree = ""; }; F0AF07051A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIConferenceHeader.strings; sourceTree = ""; }; F0AF07061A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIContactCell.strings; sourceTree = ""; }; @@ -1399,12 +1398,12 @@ D381881415FE3F0B00C3EDCA /* UICallCell.xib */, 22AA8AFF13D83F6300B30535 /* UICamSwitch.h */, 22AA8B0013D83F6300B30535 /* UICamSwitch.m */, - D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleCell.h */, - D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleCell.m */, - 639CEB081A1DF4F1004DE38F /* UIChatBubbleCell.xib */, 635173F71BA082A40095EB0A /* UIChatBubblePhotoCell.h */, 635173F81BA082A40095EB0A /* UIChatBubblePhotoCell.m */, 635173FA1BA083290095EB0A /* UIChatBubblePhotoCell.xib */, + D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleTextCell.h */, + D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */, + 63BC49D31BA1AA6F004EC273 /* UIChatBubbleTextCell.xib */, D3EA540F159853750037DC6B /* UIChatCell.h */, D3EA5410159853750037DC6B /* UIChatCell.m */, 639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */, @@ -2305,6 +2304,7 @@ 634CEE0A1B6630DB00D7A921 /* status_available.png in Resources */, 634CEDA91B6630DB00D7A921 /* call_status_outgoing.png in Resources */, 634CEE041B6630DB00D7A921 /* security_ok.png in Resources */, + 63BC49D41BA1AA6F004EC273 /* UIChatBubbleTextCell.xib in Resources */, 634CEDAC1B6630DB00D7A921 /* camera_default.png in Resources */, 634CEDB61B6630DB00D7A921 /* chat_picture_default.png in Resources */, 634CEE0E1B6630DB00D7A921 /* status_away_desktop.png in Resources */, @@ -2348,7 +2348,6 @@ 634CEDD31B6630DB00D7A921 /* history_all_default.png in Resources */, 634CEDE11B6630DB00D7A921 /* menu.png in Resources */, 634CEDAB1B6630DB00D7A921 /* call_video_start.png in Resources */, - 639CEB061A1DF4F1004DE38F /* UIChatBubbleCell.xib in Resources */, 63AADBF71B6A0FF200AA16FD /* linphonerc-factory~ipad in Resources */, 634CEDD11B6630DB00D7A921 /* footer_dialer.png in Resources */, D38187B915FE342200C3EDCA /* ContactDetailsView.xib in Resources */, @@ -2603,7 +2602,7 @@ D350F20E15A43BB100149E54 /* AssistantView.m in Sources */, D3F795D615A582810077328B /* ChatConversationView.m in Sources */, D32B6E2915A5BC440033019F /* ChatConversationTableView.m in Sources */, - D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleCell.m in Sources */, + D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleTextCell.m in Sources */, D3128FE115AABC7E00A2147A /* ContactDetailsView.m in Sources */, D37C639B15AADEF6009D0BAC /* ContactDetailsTableView.m in Sources */, 63E59A3F1ADE70D900646FB3 /* InAppProductsManager.m in Sources */, @@ -2823,15 +2822,6 @@ name = UICompositeView.xib; sourceTree = ""; }; - 639CEB081A1DF4F1004DE38F /* UIChatBubbleCell.xib */ = { - isa = PBXVariantGroup; - children = ( - 639CEB071A1DF4F1004DE38F /* Base */, - F0AF07031A24BA770086C9C1 /* ar */, - ); - name = UIChatBubbleCell.xib; - sourceTree = ""; - }; 639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */ = { isa = PBXVariantGroup; children = (