chat continuation

This commit is contained in:
Gautier Pelloux-Prayer 2015-09-10 14:35:12 +02:00
parent 877cf94d80
commit 6f49182c4c
13 changed files with 472 additions and 831 deletions

View file

@ -41,4 +41,6 @@
- (void)updateChatEntry:(LinphoneChatMessage *)chat;
- (void)setChatRoom:(LinphoneChatRoom *)room;
+ (CGSize)viewSize:(LinphoneChatMessage *)message width:(int)width;
@end

View file

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

View file

@ -21,7 +21,7 @@
#import "PhoneMainView.h"
#import "Utils.h"
#import "FileTransferDelegate.h"
#import "UIChatBubbleCell.h"
#import "UIChatBubbleTextCell.h"
@implementation ChatConversationView

View file

@ -1,160 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UIChatBubbleCell">
<connections>
<outlet property="backgroundImage" destination="3" id="11"/>
<outlet property="backgroundView" destination="7" id="10"/>
<outlet property="bubbleView" destination="16" id="30"/>
<outlet property="cancelButton" destination="fGh-KI-efm" id="R65-2g-q7P"/>
<outlet property="dateLabel" destination="22" id="24"/>
<outlet property="deleteButton" destination="18" id="19"/>
<outlet property="downloadButton" destination="33" id="34"/>
<outlet property="fileTransferProgress" destination="Pd7-2N-Jg3" id="sct-Jr-HnV"/>
<outlet property="innerView" destination="2" id="25"/>
<outlet property="messageImageView" destination="28" id="31"/>
<outlet property="messageText" destination="43" id="44"/>
<outlet property="selectedBackgroundView" destination="9" id="13"/>
<outlet property="statusImage" destination="26" id="27"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clipsSubviews="YES" tag="10" contentMode="scaleToFill" id="2" userLabel="innerView">
<rect key="frame" x="0.0" y="0.0" width="360" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="chat_bubble_incoming.png" id="3" userLabel="backgroundImage">
<rect key="frame" x="5" y="5" width="350" height="130"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<view contentMode="scaleToFill" id="16" userLabel="bubbleView">
<rect key="frame" x="13" y="13" width="334" height="114"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="29" userLabel="messageView">
<rect key="frame" x="0.0" y="0.0" width="334" height="104"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView contentMode="scaleAspectFit" id="28" userLabel="messageImageView" customClass="UILoadingImageView">
<rect key="frame" x="0.0" y="0.0" width="334" height="104"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<gestureRecognizers/>
</imageView>
<textView clipsSubviews="YES" contentMode="scaleToFill" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="They who can give up essential liberty to obtain a little temporary safety, deserve neither liberty nor safety." id="43" userLabel="messageText" customClass="UITextViewNoDefine">
<rect key="frame" x="-8" y="-11" width="350" height="126"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" label="Chat Message"/>
<color key="textColor" red="0.35686275360000003" green="0.3960784376" blue="0.43529412150000002" alpha="1" colorSpace="deviceRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="33" userLabel="downloadButton">
<rect key="frame" x="101" y="33" width="132" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Download"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" title="Download image">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onDownloadClick:" destination="-1" eventType="touchUpInside" id="39"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="fGh-KI-efm" userLabel="cancelButton">
<rect key="frame" x="101" y="34" width="132" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel transfer"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<state key="normal" title="Cancel transfer">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onCancelDownloadClick:" destination="-1" eventType="touchUpInside" id="Ag3-jA-20G"/>
</connections>
</button>
<progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" id="Pd7-2N-Jg3" userLabel="fileTransferProgress">
<rect key="frame" x="15" y="94" width="304" height="2"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</progressView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" clipsSubviews="YES" contentMode="left" text="09/09/2009 at 09:09" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" adjustsFontSizeToFit="NO" id="22" userLabel="timestampLabel">
<rect key="frame" x="44" y="104" width="274" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="9"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="chat_message_not_delivered.png" id="26" userLabel="status">
<rect key="frame" x="324" y="104" width="10" height="10"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Message status">
<bool key="isElement" value="YES"/>
</accessibility>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" id="18" userLabel="deleteButton">
<rect key="frame" x="290" y="0.0" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delete message"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="imageEdgeInsets" minX="20" minY="2" maxX="2" maxY="20"/>
<state key="normal" image="list_delete_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="highlighted" image="list_delete_over.png"/>
<connections>
<action selector="onDeleteClick:" destination="-1" eventType="touchUpInside" id="32"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<rect key="contentStretch" x="0.0" y="0.0" width="0.59999999999999964" height="1"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
<view contentMode="scaleToFill" id="7" userLabel="backgroundView">
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
<view contentMode="scaleToFill" id="9" userLabel="selectedBackgroundView">
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
</objects>
<resources>
<image name="chat_bubble_incoming.png" width="71" height="46"/>
<image name="chat_message_not_delivered.png" width="9" height="10"/>
<image name="list_delete_default.png" width="45" height="45"/>
<image name="list_delete_over.png" width="45" height="45"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>

View file

@ -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 <UIKit/UIKit.h>
#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<ChatConversationDelegate> 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

View file

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

View file

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

View file

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

View file

@ -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 <UIKit/UIKit.h>
#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<ChatConversationDelegate> chatRoomDelegate;
- (void)setChatMessage:(LinphoneChatMessage *)message;
- (IBAction)onDeleteClick:(id)event;
- (IBAction)onResendClick:(id)event;
+ (NSString *)TextMessageForChat:(LinphoneChatMessage *)message;
@end

View file

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

View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UIChatBubbleTextCell">
<connections>
<outlet property="avatarImage" destination="P1c-sD-eOv" id="h5P-hl-jbX"/>
<outlet property="bottomBarColor" destination="mlr-pl-B6T" id="4Lk-Vn-E8h"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tapGestureRecognizer id="OA2-9R-81Z" userLabel="resendClick">
<connections>
<action selector="onResendClick:" destination="-2" id="Xsy-zb-b7x"/>
</connections>
</tapGestureRecognizer>
<view contentMode="scaleToFill" id="vdk-RV-QRU">
<rect key="frame" x="0.0" y="0.0" width="320" height="159"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" alpha="0.19999999999999968" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="q2R-Zv-e5a" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="320" height="159"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" usesAttributedText="YES" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="GRe-ur-aSb" userLabel="contactDateLabel">
<rect key="frame" x="66" y="8" width="246" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact name"/>
<attributedString key="attributedText">
<fragment content="11:35 John ">
<attributes>
<color key="NSColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<font key="NSFont" size="16" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="truncatingTail" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="Doe">
<attributes>
<color key="NSColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<font key="NSFont" size="16" name="HelveticaNeue-Bold"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="truncatingTail" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="avatar.png" id="P1c-sD-eOv" userLabel="avatarImage" customClass="UIRoundedImageView">
<rect key="frame" x="8" y="5" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contact avatar">
<accessibilityTraits key="traits" none="YES" image="YES" notEnabled="YES"/>
<bool key="isElement" value="YES"/>
</accessibility>
</imageView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Placeholder text is another very long ipsem lum dolorem...and it continues" id="CYa-If-oB4" userLabel="messageText" customClass="UITextViewNoDefine">
<rect key="frame" x="66" y="30" width="246" height="121"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="chat_message_inprogress.png" id="nLy-JO-TyL" userLabel="statusImage">
<rect key="frame" x="305" y="0.0" width="15" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="mlr-pl-B6T" userLabel="bottomBarColor">
<rect key="frame" x="0.0" y="157" width="320" height="2"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="339" y="43.5"/>
</view>
</objects>
<resources>
<image name="avatar.png" width="261" height="261"/>
<image name="chat_message_inprogress.png" width="12" height="12"/>
<image name="color_A.png" width="1" height="1"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>

View file

@ -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 = "<group>"; };
639CEB011A1DF4E4004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIHistoryCell.xib; sourceTree = "<group>"; };
639CEB041A1DF4EB004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UICompositeView.xib; sourceTree = "<group>"; };
639CEB071A1DF4F1004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatBubbleCell.xib; sourceTree = "<group>"; };
639CEB0A1A1DF4FA004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatCell.xib; sourceTree = "<group>"; };
639CEB0C1A1DF528004DE38F /* fr */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UICallCell.strings; sourceTree = "<group>"; };
639CEB0D1A1DF52C004DE38F /* ru */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/UICallCell.strings; sourceTree = "<group>"; };
@ -896,6 +895,7 @@
63B81A091B57DA33009604A6 /* TPKeyboardAvoidingTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyboardAvoidingTableView.m; sourceTree = "<group>"; };
63B81A0A1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+TPKeyboardAvoidingAdditions.h"; sourceTree = "<group>"; };
63B81A0B1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+TPKeyboardAvoidingAdditions.m"; sourceTree = "<group>"; };
63BC49D31BA1AA6F004EC273 /* UIChatBubbleTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UIChatBubbleTextCell.xib; sourceTree = "<group>"; };
63CD4B4D1A5AAC8C00B84282 /* DTAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTAlertView.h; sourceTree = "<group>"; };
63CD4B4E1A5AAC8C00B84282 /* DTAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DTAlertView.m; sourceTree = "<group>"; };
63CFEDE21B9EDD36007EA5BD /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = "liblinphone-sdk/apple-darwin/lib/libswresample.a"; sourceTree = "<group>"; };
@ -1013,8 +1013,8 @@
D380801215C299D0005BE9BC /* ColorSpaceUtilites.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ColorSpaceUtilites.m; path = Utils/ColorSpaceUtilites.m; sourceTree = "<group>"; };
D3A55FBA15877E5E003FD403 /* UIContactCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIContactCell.h; sourceTree = "<group>"; };
D3A55FBB15877E5E003FD403 /* UIContactCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIContactCell.m; sourceTree = "<group>"; };
D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatBubbleCell.h; sourceTree = "<group>"; };
D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatBubbleCell.m; sourceTree = "<group>"; };
D3A8BB6E15A6C7D500F96BE5 /* UIChatBubbleTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatBubbleTextCell.h; sourceTree = "<group>"; };
D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatBubbleTextCell.m; sourceTree = "<group>"; };
D3C6526515AC1A8F0092A874 /* UIEditableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIEditableTableViewCell.h; sourceTree = "<group>"; };
D3C6526615AC1A8F0092A874 /* UIEditableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIEditableTableViewCell.m; sourceTree = "<group>"; };
D3EA53FB159850E80037DC6B /* LinphoneManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinphoneManager.h; sourceTree = "<group>"; };
@ -1118,7 +1118,6 @@
F0AF06FD1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/CallIncomingView.strings; sourceTree = "<group>"; };
F0AF07011A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UICallCell.strings; sourceTree = "<group>"; };
F0AF07021A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIChatCell.strings; sourceTree = "<group>"; };
F0AF07031A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIChatBubbleCell.strings; sourceTree = "<group>"; };
F0AF07041A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UICompositeView.strings; sourceTree = "<group>"; };
F0AF07051A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIConferenceHeader.strings; sourceTree = "<group>"; };
F0AF07061A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIContactCell.strings; sourceTree = "<group>"; };
@ -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 = "<group>";
};
639CEB081A1DF4F1004DE38F /* UIChatBubbleCell.xib */ = {
isa = PBXVariantGroup;
children = (
639CEB071A1DF4F1004DE38F /* Base */,
F0AF07031A24BA770086C9C1 /* ar */,
);
name = UIChatBubbleCell.xib;
sourceTree = "<group>";
};
639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */ = {
isa = PBXVariantGroup;
children = (