ChatConversation: optimize conversation loading and fix UI tests

This commit is contained in:
Gautier Pelloux-Prayer 2015-11-16 11:05:06 +01:00
parent 94bdcd7676
commit 50f0a56a4f
12 changed files with 146 additions and 128 deletions

View file

@ -148,7 +148,7 @@
<rect key="frame" x="0.0" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<animations/>
<accessibility key="accessibilityConfiguration" label="Delete all"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
<state key="normal" image="cancel_edit_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>

View file

@ -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;
}

View file

@ -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 <UITextFieldDelegate> {
@private

View file

@ -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;

View file

@ -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;

View file

@ -25,7 +25,7 @@
#import <AssetsLibrary/ALAssetRepresentation.h>
@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

View file

@ -15,6 +15,7 @@
<outlet property="contactDateLabel" destination="JyR-RQ-uwF" id="Tc4-9t-i5V"/>
<outlet property="downloadButton" destination="N75-gL-R6t" id="EgN-Ab-Ded"/>
<outlet property="fileTransferProgress" destination="USm-wC-GvG" id="POt-YD-NCG"/>
<outlet property="imageGestureRecognizer" destination="aDF-hC-ddO" id="2jh-Rr-eKk"/>
<outlet property="imageSubView" destination="GmN-7v-uuO" id="k9r-Xc-csv"/>
<outlet property="messageImageView" destination="yMW-cT-bpU" id="MNr-F2-abQ"/>
<outlet property="resendRecognizer" destination="5ZI-Ip-lGl" id="G2r-On-6mV"/>
@ -119,7 +120,7 @@
<rect key="frame" x="302" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<animations/>
<accessibility key="accessibilityConfiguration" label="Message status"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" id="Eab-ND-ix3" userLabel="statusInprogressSpinner">
<rect key="frame" x="302" y="0.0" width="20" height="20"/>

View file

@ -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

View file

@ -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;

View file

@ -67,7 +67,7 @@
<rect key="frame" x="302" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<animations/>
<accessibility key="accessibilityConfiguration" label="Message status"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" id="4Z8-PE-uPe" userLabel="statusInprogressSpinner">
<rect key="frame" x="302" y="0.0" width="20" height="20"/>

View file

@ -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;

View file

@ -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];