mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-28 16:49:20 +00:00
chat continuation
This commit is contained in:
parent
877cf94d80
commit
6f49182c4c
13 changed files with 472 additions and 831 deletions
|
|
@ -41,4 +41,6 @@
|
|||
- (void)updateChatEntry:(LinphoneChatMessage *)chat;
|
||||
- (void)setChatRoom:(LinphoneChatRoom *)room;
|
||||
|
||||
+ (CGSize)viewSize:(LinphoneChatMessage *)message width:(int)width;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#import "PhoneMainView.h"
|
||||
#import "Utils.h"
|
||||
#import "FileTransferDelegate.h"
|
||||
#import "UIChatBubbleCell.h"
|
||||
#import "UIChatBubbleTextCell.h"
|
||||
|
||||
@implementation ChatConversationView
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
47
Classes/LinphoneUI/UIChatBubbleTextCell.h
Normal file
47
Classes/LinphoneUI/UIChatBubbleTextCell.h
Normal 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
|
||||
235
Classes/LinphoneUI/UIChatBubbleTextCell.m
Normal file
235
Classes/LinphoneUI/UIChatBubbleTextCell.m
Normal 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
|
||||
89
Classes/LinphoneUI/UIChatBubbleTextCell.xib
Normal file
89
Classes/LinphoneUI/UIChatBubbleTextCell.xib
Normal 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>
|
||||
Binary file not shown.
|
|
@ -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 = (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue