From 7b02207537d35bfa1714bf8beafc921f717d100a Mon Sep 17 00:00:00 2001
From: 单军华
Date: Wed, 11 Jul 2018 10:47:42 +0800
Subject: [PATCH] 首次上传

---
 screendisplay/Pods/EaseUI/EaseUI/EMUIKit/ViewController/EaseMessageViewController.m | 2171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2,171 insertions(+), 0 deletions(-)

diff --git a/screendisplay/Pods/EaseUI/EaseUI/EMUIKit/ViewController/EaseMessageViewController.m b/screendisplay/Pods/EaseUI/EaseUI/EMUIKit/ViewController/EaseMessageViewController.m
new file mode 100755
index 0000000..9385c3e
--- /dev/null
+++ b/screendisplay/Pods/EaseUI/EaseUI/EMUIKit/ViewController/EaseMessageViewController.m
@@ -0,0 +1,2171 @@
+/************************************************************
+ *  * Hyphenate CONFIDENTIAL
+ * __________________
+ * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
+ *
+ * NOTICE: All information contained herein is, and remains
+ * the property of Hyphenate Inc.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Hyphenate Inc.
+ */
+
+#import "EaseMessageViewController.h"
+
+#import <Foundation/Foundation.h>
+#import <Photos/Photos.h>
+#import <AssetsLibrary/AssetsLibrary.h>
+
+#import "NSDate+Category.h"
+#import "EaseUsersListViewController.h"
+#import "EaseMessageReadManager.h"
+#import "EaseEmotionManager.h"
+#import "EaseEmoji.h"
+#import "EaseEmotionEscape.h"
+#import "EaseCustomMessageCell.h"
+#import "UIImage+GIF.h"
+#import "EaseLocalDefine.h"
+#import "EaseSDKHelper.h"
+
+#define KHintAdjustY    50
+
+#define IOS_VERSION [[UIDevice currentDevice] systemVersion]>=9.0
+
+typedef enum : NSUInteger {
+    EMRequestRecord,
+    EMCanRecord,
+    EMCanNotRecord,
+} EMRecordResponse;
+
+
+@implementation EaseAtTarget
+- (instancetype)initWithUserId:(NSString*)userId andNickname:(NSString*)nickname
+{
+    if (self = [super init]) {
+        _userId = [userId copy];
+        _nickname = [nickname copy];
+    }
+    return self;
+}
+@end
+
+@interface EaseMessageViewController ()<EaseMessageCellDelegate>
+{
+    UIMenuItem *_copyMenuItem;
+    UIMenuItem *_deleteMenuItem;
+    UILongPressGestureRecognizer *_lpgr;
+    NSMutableArray *_atTargets;
+    
+    dispatch_queue_t _messageQueue;
+    BOOL _isRecording;
+}
+
+@property (strong, nonatomic) id<IMessageModel> playingVoiceModel;
+@property (nonatomic) BOOL isKicked;
+@property (nonatomic) BOOL isPlayingAudio;
+@property (nonatomic, strong) NSMutableArray *atTargets;
+
+@end
+
+@implementation EaseMessageViewController
+
+@synthesize conversation = _conversation;
+@synthesize deleteConversationIfNull = _deleteConversationIfNull;
+@synthesize messageCountOfPage = _messageCountOfPage;
+@synthesize timeCellHeight = _timeCellHeight;
+@synthesize messageTimeIntervalTag = _messageTimeIntervalTag;
+
+- (instancetype)initWithConversationChatter:(NSString *)conversationChatter
+                           conversationType:(EMConversationType)conversationType
+{
+    if ([conversationChatter length] == 0) {
+        return nil;
+    }
+    
+    self = [super initWithStyle:UITableViewStylePlain];
+    if (self) {
+        _conversation = [[EMClient sharedClient].chatManager getConversation:conversationChatter type:conversationType createIfNotExist:YES];
+        
+        _messageCountOfPage = 10;
+        _timeCellHeight = 30;
+        _deleteConversationIfNull = YES;
+        _scrollToBottomWhenAppear = YES;
+        _messsagesSource = [NSMutableArray array];
+        
+        [_conversation markAllMessagesAsRead:nil];
+    }
+    
+    return self;
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    self.view.backgroundColor = [UIColor colorWithRed:248 / 255.0 green:248 / 255.0 blue:248 / 255.0 alpha:1.0];
+    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hideImagePicker) name:@"hideImagePicker" object:nil];
+    
+    //Initialization
+    CGFloat chatbarHeight = [EaseChatToolbar defaultHeight];
+    EMChatToolbarType barType = self.conversation.type == EMConversationTypeChat ? EMChatToolbarTypeChat : EMChatToolbarTypeGroup;
+    self.chatToolbar = [[EaseChatToolbar alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - chatbarHeight - iPhoneX_BOTTOM_HEIGHT, self.view.frame.size.width, chatbarHeight) type:barType];
+    self.chatToolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
+    
+    //Initializa the gesture recognizer
+    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(keyBoardHidden:)];
+    [self.view addGestureRecognizer:tap];
+    
+    _lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
+    _lpgr.minimumPressDuration = 0.5;
+    [self.tableView addGestureRecognizer:_lpgr];
+    
+    _messageQueue = dispatch_queue_create("hyphenate.com", NULL);
+    
+    //Register the delegate
+    [EMCDDeviceManager sharedInstance].delegate = self;
+    [[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
+    [[EMClient sharedClient].roomManager addDelegate:self delegateQueue:nil];
+    
+    if (self.conversation.type == EMConversationTypeChatRoom)
+    {
+        [self joinChatroom:self.conversation.conversationId];
+    }
+    
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(didBecomeActive)
+                                                 name:UIApplicationDidBecomeActiveNotification
+                                               object:nil];
+    
+    [[EaseBaseMessageCell appearance] setSendBubbleBackgroundImage:[[UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_bg"] stretchableImageWithLeftCapWidth:5 topCapHeight:35]];
+    [[EaseBaseMessageCell appearance] setRecvBubbleBackgroundImage:[[UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_bg"] stretchableImageWithLeftCapWidth:35 topCapHeight:35]];
+    
+    [[EaseBaseMessageCell appearance] setSendMessageVoiceAnimationImages:@[[UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_full"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_000"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_001"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_002"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_003"]]];
+    [[EaseBaseMessageCell appearance] setRecvMessageVoiceAnimationImages:@[[UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing_full"],[UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing000"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing001"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing002"], [UIImage easeImageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing003"]]];
+    
+    [[EaseBaseMessageCell appearance] setAvatarSize:40.f];
+    [[EaseBaseMessageCell appearance] setAvatarCornerRadius:20.f];
+    
+    [[EaseChatBarMoreView appearance] setMoreViewBackgroundColor:[UIColor colorWithRed:240 / 255.0 green:242 / 255.0 blue:247 / 255.0 alpha:1.0]];
+    
+    [self tableViewDidTriggerHeaderRefresh];
+    [self setupEmotion];
+    
+    self.tableView.estimatedRowHeight = 0;
+    self.tableView.estimatedSectionHeaderHeight = 0;
+    self.tableView.estimatedSectionFooterHeight = 0;
+}
+
+/*!
+ @method
+ @brief ������������
+ @discussion ������������������������������������������dataSource���������������������������������������������������������
+ @result
+ */
+- (void)setupEmotion
+{
+    if ([self.dataSource respondsToSelector:@selector(emotionFormessageViewController:)]) {
+        NSArray* emotionManagers = [self.dataSource emotionFormessageViewController:self];
+        [self.faceView setEmotionManagers:emotionManagers];
+    } else {
+        NSMutableArray *emotions = [NSMutableArray array];
+        for (NSString *name in [EaseEmoji allEmoji]) {
+            EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name emotionThumbnail:name emotionOriginal:name emotionOriginalURL:@"" emotionType:EMEmotionDefault];
+            [emotions addObject:emotion];
+        }
+        EaseEmotion *emotion = [emotions objectAtIndex:0];
+        EaseEmotionManager *manager= [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:emotions tagImage:[UIImage imageNamed:emotion.emotionId]];
+        [self.faceView setEmotionManagers:@[manager]];
+    }
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+- (void)dealloc
+{
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    
+    [[EMCDDeviceManager sharedInstance] stopPlaying];
+    [EMCDDeviceManager sharedInstance].delegate = nil;
+    
+    if (_imagePicker){
+        [_imagePicker dismissViewControllerAnimated:NO completion:nil];
+        _imagePicker = nil;
+    }
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+    [super viewWillAppear:animated];
+    
+    self.isViewDidAppear = YES;
+    [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
+    
+    if (self.scrollToBottomWhenAppear) {
+        [self _scrollViewToBottom:NO];
+    }
+    self.scrollToBottomWhenAppear = YES;
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    [super viewWillDisappear:animated];
+    
+    self.isViewDidAppear = NO;
+    [[EMCDDeviceManager sharedInstance] disableProximitySensor];
+}
+
+#pragma mark - chatroom
+
+- (void)saveChatroom:(EMChatroom *)chatroom
+{
+    NSString *chatroomName = chatroom.subject ? chatroom.subject : @"";
+    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+    NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
+    NSMutableDictionary *chatRooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
+    if (![chatRooms objectForKey:chatroom.chatroomId])
+    {
+        [chatRooms setObject:chatroomName forKey:chatroom.chatroomId];
+        [ud setObject:chatRooms forKey:key];
+        [ud synchronize];
+    }
+}
+
+/*!
+ @method
+ @brief ���������������
+ @discussion
+ @result
+ */
+- (void)joinChatroom:(NSString *)chatroomId
+{
+    __weak typeof(self) weakSelf = self;
+    [self showHudInView:self.view hint:NSEaseLocalizedString(@"chatroom.joining",@"Joining the chatroom")];
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        EMError *error = nil;
+        EMChatroom *chatroom = [[EMClient sharedClient].roomManager joinChatroom:chatroomId error:&error];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (weakSelf) {
+                EaseMessageViewController *strongSelf = weakSelf;
+                [strongSelf hideHud];
+                if (error != nil) {
+                    [strongSelf showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.joinFailed",@"join chatroom \'%@\' failed"), chatroomId]];
+                } else {
+                    strongSelf.isJoinedChatroom = YES;
+                    [strongSelf saveChatroom:chatroom];
+                }
+            }  else {
+                if (!error || (error.code == EMErrorChatroomAlreadyJoined)) {
+                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                        EMError *leaveError;
+                        [[EMClient sharedClient].roomManager leaveChatroom:chatroomId error:&leaveError];
+                        [[EMClient sharedClient].chatManager deleteConversation:chatroomId isDeleteMessages:YES completion:nil];
+                    });
+                }
+            }
+        });
+    });
+}
+
+#pragma mark - EMChatManagerChatroomDelegate
+
+- (void)didReceiveUserJoinedChatroom:(EMChatroom *)aChatroom
+                            username:(NSString *)aUsername
+{
+    CGRect frame = self.chatToolbar.frame;
+    [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.join", @"\'%@\'join chatroom\'%@\'"), aUsername, aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
+}
+
+- (void)didReceiveUserLeavedChatroom:(EMChatroom *)aChatroom
+                            username:(NSString *)aUsername
+{
+    CGRect frame = self.chatToolbar.frame;
+    [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.leave.hint", @"\'%@\'leave chatroom\'%@\'"), aUsername, aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
+}
+
+- (void)didReceiveKickedFromChatroom:(EMChatroom *)aChatroom
+                              reason:(EMChatroomBeKickedReason)aReason
+{
+    if ([_conversation.conversationId isEqualToString:aChatroom.chatroomId])
+    {
+        _isKicked = YES;
+        CGRect frame = self.chatToolbar.frame;
+        [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.remove", @"be removed from chatroom\'%@\'"), aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
+        [self.navigationController popToViewController:self animated:NO];
+        [self.navigationController popViewControllerAnimated:YES];
+    }
+}
+
+#pragma mark - getter
+
+- (UIImagePickerController *)imagePicker
+{
+    if (_imagePicker == nil) {
+        _imagePicker = [[UIImagePickerController alloc] init];
+        _imagePicker.modalPresentationStyle= UIModalPresentationOverFullScreen;
+        _imagePicker.delegate = self;
+    }
+    
+    return _imagePicker;
+}
+
+- (NSMutableArray*)atTargets
+{
+    if (!_atTargets) {
+        _atTargets = [NSMutableArray array];
+    }
+    return _atTargets;
+}
+
+#pragma mark - setter
+
+- (void)setIsViewDidAppear:(BOOL)isViewDidAppear
+{
+    _isViewDidAppear =isViewDidAppear;
+    if (_isViewDidAppear)
+    {
+        NSMutableArray *unreadMessages = [NSMutableArray array];
+        for (EMMessage *message in self.messsagesSource)
+        {
+            if ([self shouldSendHasReadAckForMessage:message read:NO])
+            {
+                [unreadMessages addObject:message];
+            }
+        }
+        if ([unreadMessages count])
+        {
+            [self _sendHasReadResponseForMessages:unreadMessages isRead:YES];
+        }
+        
+        [_conversation markAllMessagesAsRead:nil];
+    }
+}
+
+- (void)setChatToolbar:(EaseChatToolbar *)chatToolbar
+{
+    [_chatToolbar removeFromSuperview];
+    
+    _chatToolbar = chatToolbar;
+    if (_chatToolbar) {
+        [self.view addSubview:_chatToolbar];
+    }
+    
+    CGRect tableFrame = self.tableView.frame;
+    tableFrame.size.height = self.view.frame.size.height - _chatToolbar.frame.size.height - iPhoneX_BOTTOM_HEIGHT;
+    self.tableView.frame = tableFrame;
+    if ([chatToolbar isKindOfClass:[EaseChatToolbar class]]) {
+        [(EaseChatToolbar *)self.chatToolbar setDelegate:self];
+        self.chatBarMoreView = (EaseChatBarMoreView*)[(EaseChatToolbar *)self.chatToolbar moreView];
+        self.faceView = (EaseFaceView*)[(EaseChatToolbar *)self.chatToolbar faceView];
+        self.recordView = (EaseRecordView*)[(EaseChatToolbar *)self.chatToolbar recordView];
+    }
+}
+
+- (void)setDataSource:(id<EaseMessageViewControllerDataSource>)dataSource
+{
+    _dataSource = dataSource;
+    
+    [self setupEmotion];
+}
+
+- (void)setDelegate:(id<EaseMessageViewControllerDelegate>)delegate
+{
+    _delegate = delegate;
+}
+
+#pragma mark - private helper
+
+/*!
+ @method
+ @brief tableView���������������
+ @discussion
+ @result
+ */
+- (void)_scrollViewToBottom:(BOOL)animated
+{
+    if (self.tableView.contentSize.height > self.tableView.frame.size.height)
+    {
+        CGPoint offset = CGPointMake(0, self.tableView.contentSize.height - self.tableView.frame.size.height);
+        [self.tableView setContentOffset:offset animated:animated];
+    }
+}
+
+/*!
+ @method
+ @brief ������������������������������
+ @discussion
+ @param aCompletion ������������
+ @result
+ */
+- (void)_canRecordCompletion:(void(^)(EMRecordResponse))aCompletion
+{
+    AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
+    if (videoAuthStatus == AVAuthorizationStatusNotDetermined) {
+        [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
+            if (aCompletion) {
+                aCompletion(granted ? EMCanRecord : EMRequestRecord);
+            }
+        }];
+    }
+    else if(videoAuthStatus == AVAuthorizationStatusRestricted || videoAuthStatus == AVAuthorizationStatusDenied) {
+        aCompletion(EMCanNotRecord);
+    }
+    else{
+        aCompletion(EMCanRecord);
+    }
+}
+
+- (void)showMenuViewController:(UIView *)showInView
+                  andIndexPath:(NSIndexPath *)indexPath
+                   messageType:(EMMessageBodyType)messageType
+{
+    if (_menuController == nil) {
+        _menuController = [UIMenuController sharedMenuController];
+    }
+    
+    if (_deleteMenuItem == nil) {
+        _deleteMenuItem = [[UIMenuItem alloc] initWithTitle:NSEaseLocalizedString(@"delete", @"Delete") action:@selector(deleteMenuAction:)];
+    }
+    
+    if (_copyMenuItem == nil) {
+        _copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSEaseLocalizedString(@"copy", @"Copy") action:@selector(copyMenuAction:)];
+    }
+    
+    if (messageType == EMMessageBodyTypeText) {
+        [_menuController setMenuItems:@[_copyMenuItem, _deleteMenuItem]];
+    } else {
+        [_menuController setMenuItems:@[_deleteMenuItem]];
+    }
+    [_menuController setTargetRect:showInView.frame inView:showInView.superview];
+    [_menuController setMenuVisible:YES animated:YES];
+}
+
+- (void)_stopAudioPlayingWithChangeCategory:(BOOL)isChange
+{
+    //���������������������������������
+    [[EMCDDeviceManager sharedInstance] stopPlaying];
+    [[EMCDDeviceManager sharedInstance] disableProximitySensor];
+    [EMCDDeviceManager sharedInstance].delegate = nil;
+    
+    //    MessageModel *playingModel = [self.EaseMessageReadManager stopMessageAudioModel];
+    //    NSIndexPath *indexPath = nil;
+    //    if (playingModel) {
+    //        indexPath = [NSIndexPath indexPathForRow:[self.dataSource indexOfObject:playingModel] inSection:0];
+    //    }
+    //
+    //    if (indexPath) {
+    //        dispatch_async(dispatch_get_main_queue(), ^{
+    //            [self.tableView beginUpdates];
+    //            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
+    //            [self.tableView endUpdates];
+    //        });
+    //    }
+}
+
+/*!
+ @method
+ @brief mov���������������������MP4������
+ @discussion
+ @param movUrl   mov������������
+ @result  MP4������������������
+ */
+- (NSURL *)_convert2Mp4:(NSURL *)movUrl
+{
+    NSURL *mp4Url = nil;
+    AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:movUrl options:nil];
+    NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
+    
+    if ([compatiblePresets containsObject:AVAssetExportPresetHighestQuality]) {
+        AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset
+                                                                              presetName:AVAssetExportPresetHighestQuality];
+        NSString *mp4Path = [NSString stringWithFormat:@"%@/%d%d.mp4", [EMCDDeviceManager dataPath], (int)[[NSDate date] timeIntervalSince1970], arc4random() % 100000];
+        mp4Url = [NSURL fileURLWithPath:mp4Path];
+        exportSession.outputURL = mp4Url;
+        exportSession.shouldOptimizeForNetworkUse = YES;
+        exportSession.outputFileType = AVFileTypeMPEG4;
+        dispatch_semaphore_t wait = dispatch_semaphore_create(0l);
+        [exportSession exportAsynchronouslyWithCompletionHandler:^{
+            switch ([exportSession status]) {
+                case AVAssetExportSessionStatusFailed: {
+                    NSLog(@"failed, error:%@.", exportSession.error);
+                } break;
+                case AVAssetExportSessionStatusCancelled: {
+                    NSLog(@"cancelled.");
+                } break;
+                case AVAssetExportSessionStatusCompleted: {
+                    NSLog(@"completed.");
+                } break;
+                default: {
+                    NSLog(@"others.");
+                } break;
+            }
+            dispatch_semaphore_signal(wait);
+        }];
+        long timeout = dispatch_semaphore_wait(wait, DISPATCH_TIME_FOREVER);
+        if (timeout) {
+            NSLog(@"timeout.");
+        }
+        if (wait) {
+            //dispatch_release(wait);
+            wait = nil;
+        }
+    }
+    
+    return mp4Url;
+}
+
+/*!
+ @method
+ @brief ���������������������������������������������������
+ @discussion
+ @result
+ */
+- (EMChatType)_messageTypeFromConversationType
+{
+    EMChatType type = EMChatTypeChat;
+    switch (self.conversation.type) {
+        case EMConversationTypeChat:
+            type = EMChatTypeChat;
+            break;
+        case EMConversationTypeGroupChat:
+            type = EMChatTypeGroupChat;
+            break;
+        case EMConversationTypeChatRoom:
+            type = EMChatTypeChatRoom;
+            break;
+        default:
+            break;
+    }
+    return type;
+}
+
+- (void)_customDownloadMessageFile:(EMMessage *)aMessage
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"message.autoTransfer", @"Please customize the  transfer attachment method") delegate:nil cancelButtonTitle:NSLocalizedString(@"sure", @"OK") otherButtonTitles:nil, nil];
+        [alertView show];
+    });
+}
+
+/*!
+ @method
+ @brief ������������������
+ @discussion
+ @param message  ������������������������
+ @result
+ */
+- (void)_downloadMessageAttachments:(EMMessage *)message
+{
+    __weak typeof(self) weakSelf = self;
+    void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
+        if (!error)
+        {
+            [weakSelf _reloadTableViewDataWithMessage:message];
+        }
+        else
+        {
+            [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
+        }
+    };
+    
+    BOOL isCustomDownload = !([EMClient sharedClient].options.isAutoTransferMessageAttachments);
+    BOOL isAutoDownloadThumbnail = ([EMClient sharedClient].options.isAutoDownloadThumbnail);
+    EMMessageBody *messageBody = message.body;
+    if ([messageBody type] == EMMessageBodyTypeImage) {
+        EMImageMessageBody *imageBody = (EMImageMessageBody *)messageBody;
+        if (imageBody.thumbnailDownloadStatus > EMDownloadStatusSuccessed)
+        {
+            //download the message thumbnail
+            if (isCustomDownload) {
+                [self _customDownloadMessageFile:message];
+            } else {
+                if (isAutoDownloadThumbnail) {
+                    [[[EMClient sharedClient] chatManager] downloadMessageThumbnail:message progress:nil completion:completion];
+                }
+            }
+        }
+    }
+    else if ([messageBody type] == EMMessageBodyTypeVideo)
+    {
+        EMVideoMessageBody *videoBody = (EMVideoMessageBody *)messageBody;
+        if (videoBody.thumbnailDownloadStatus > EMDownloadStatusSuccessed)
+        {
+            //download the message thumbnail
+            if (isCustomDownload) {
+                [self _customDownloadMessageFile:message];
+            } else {
+                if (isAutoDownloadThumbnail) {
+                    [[[EMClient sharedClient] chatManager] downloadMessageThumbnail:message progress:nil completion:completion];
+                }
+            }
+        }
+    }
+    else if ([messageBody type] == EMMessageBodyTypeVoice)
+    {
+        EMVoiceMessageBody *voiceBody = (EMVoiceMessageBody*)messageBody;
+        if (voiceBody.downloadStatus > EMDownloadStatusSuccessed)
+        {
+            //download the message attachment
+            if (isCustomDownload) {
+                [self _customDownloadMessageFile:message];
+            } else {
+                if (isAutoDownloadThumbnail) {
+                    [[EMClient sharedClient].chatManager downloadMessageAttachment:message progress:nil completion:^(EMMessage *message, EMError *error) {
+                        if (!error) {
+                            [weakSelf _reloadTableViewDataWithMessage:message];
+                        }
+                        else {
+                            [weakSelf showHint:NSEaseLocalizedString(@"message.voiceFail", @"voice for failure!")];
+                        }
+                    }];
+                }
+            }
+        }
+    }
+}
+
+/*!
+ @method
+ @brief ������������������������������������������
+ @discussion
+ @param message  ������������������
+ @param read     ������������������
+ @result
+ */
+- (BOOL)shouldSendHasReadAckForMessage:(EMMessage *)message
+                                  read:(BOOL)read
+{
+    if (message.chatType != EMChatTypeChat || message.isReadAcked || message.direction == EMMessageDirectionSend || ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) || !self.isViewDidAppear)
+    {
+        return NO;
+    }
+    
+    EMMessageBody *body = message.body;
+    if (((body.type == EMMessageBodyTypeVideo) ||
+         (body.type == EMMessageBodyTypeVoice) ||
+         (body.type == EMMessageBodyTypeImage)) &&
+        !read)
+    {
+        return NO;
+    }
+    else
+    {
+        return YES;
+    }
+}
+
+/*!
+ @method
+ @brief ������������������������������������
+ @discussion
+ @param messages  ������������������������������������
+ @param isRead    ������������
+ @result
+ */
+- (void)_sendHasReadResponseForMessages:(NSArray*)messages
+                                 isRead:(BOOL)isRead
+{
+    NSMutableArray *unreadMessages = [NSMutableArray array];
+    for (NSInteger i = 0; i < [messages count]; i++)
+    {
+        EMMessage *message = messages[i];
+        BOOL isSend = YES;
+        if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:shouldSendHasReadAckForMessage:read:)]) {
+            isSend = [_dataSource messageViewController:self
+                         shouldSendHasReadAckForMessage:message read:isRead];
+        }
+        else{
+            isSend = [self shouldSendHasReadAckForMessage:message
+                                                     read:isRead];
+        }
+        
+        if (isSend)
+        {
+            [unreadMessages addObject:message];
+        }
+    }
+    
+    if ([unreadMessages count])
+    {
+        for (EMMessage *message in unreadMessages)
+        {
+            [[EMClient sharedClient].chatManager sendMessageReadAck:message completion:nil];
+        }
+    }
+}
+
+- (BOOL)_shouldMarkMessageAsRead
+{
+    BOOL isMark = YES;
+    if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewControllerShouldMarkMessagesAsRead:)]) {
+        isMark = [_dataSource messageViewControllerShouldMarkMessagesAsRead:self];
+    }
+    else{
+        if (([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) || !self.isViewDidAppear)
+        {
+            isMark = NO;
+        }
+    }
+    
+    return isMark;
+}
+
+/*!
+ @method
+ @brief ���������������������������
+ @discussion
+ @param model ������model
+ @result
+ */
+- (void)_locationMessageCellSelected:(id<IMessageModel>)model
+{
+    _scrollToBottomWhenAppear = NO;
+    
+    EaseLocationViewController *locationController = [[EaseLocationViewController alloc] initWithLocation:CLLocationCoordinate2DMake(model.latitude, model.longitude)];
+    [self.navigationController pushViewController:locationController animated:YES];
+}
+
+/*!
+ @method
+ @brief ���������������������������
+ @discussion
+ @param model ������model
+ @result
+ */
+- (void)_videoMessageCellSelected:(id<IMessageModel>)model
+{
+    _scrollToBottomWhenAppear = NO;
+    
+    EMVideoMessageBody *videoBody = (EMVideoMessageBody*)model.message.body;
+    
+    NSString *localPath = [model.fileLocalPath length] > 0 ? model.fileLocalPath : videoBody.localPath;
+    if ([localPath length] == 0) {
+        [self showHint:NSEaseLocalizedString(@"message.videoFail", @"video for failure!")];
+        return;
+    }
+    
+    dispatch_block_t block = ^{
+        //send the acknowledgement
+        [self _sendHasReadResponseForMessages:@[model.message]
+                                       isRead:YES];
+        
+        NSURL *videoURL = [NSURL fileURLWithPath:localPath];
+        MPMoviePlayerViewController *moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
+        [moviePlayerController.moviePlayer prepareToPlay];
+        moviePlayerController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
+        [self presentMoviePlayerViewControllerAnimated:moviePlayerController];
+    };
+    
+    BOOL isCustomDownload = !([EMClient sharedClient].options.isAutoTransferMessageAttachments);
+    __weak typeof(self) weakSelf = self;
+    void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
+        if (!error)
+        {
+            [weakSelf _reloadTableViewDataWithMessage:aMessage];
+        }
+        else
+        {
+            [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
+        }
+    };
+    
+    if (videoBody.thumbnailDownloadStatus == EMDownloadStatusFailed || ![[NSFileManager defaultManager] fileExistsAtPath:videoBody.thumbnailLocalPath]) {
+        [self showHint:@"begin downloading thumbnail image, click later"];
+        if (isCustomDownload) {
+            [self _customDownloadMessageFile:model.message];
+        } else {
+            [[EMClient sharedClient].chatManager downloadMessageThumbnail:model.message progress:nil completion:completion];
+        }
+        return;
+    }
+    
+    if (videoBody.downloadStatus == EMDownloadStatusSuccessed && [[NSFileManager defaultManager] fileExistsAtPath:localPath])
+    {
+        block();
+        return;
+    }
+    
+    [self showHudInView:self.view hint:NSEaseLocalizedString(@"message.downloadingVideo", @"downloading video...")];
+    if (isCustomDownload) {
+        [self _customDownloadMessageFile:model.message];
+    } else {
+        [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
+            [weakSelf hideHud];
+            if (!error) {
+                block();
+            }else{
+                [weakSelf showHint:NSEaseLocalizedString(@"message.videoFail", @"video for failure!")];
+            }
+        }];
+    }
+}
+
+/*!
+ @method
+ @brief ���������������������������
+ @discussion
+ @param model ������model
+ @result
+ */
+- (void)_imageMessageCellSelected:(id<IMessageModel>)model
+{
+    __weak EaseMessageViewController *weakSelf = self;
+    EMImageMessageBody *imageBody = (EMImageMessageBody*)[model.message body];
+    
+    BOOL isCustomDownload = !([EMClient sharedClient].options.isAutoTransferMessageAttachments);
+    if ([imageBody type] == EMMessageBodyTypeImage) {
+        if (imageBody.thumbnailDownloadStatus == EMDownloadStatusSuccessed) {
+            if (imageBody.downloadStatus == EMDownloadStatusSuccessed)
+            {
+                //send the acknowledgement
+                [weakSelf _sendHasReadResponseForMessages:@[model.message] isRead:YES];
+                NSString *localPath = model.message == nil ? model.fileLocalPath : [imageBody localPath];
+                if (localPath && localPath.length > 0) {
+                    UIImage *image = [UIImage imageWithContentsOfFile:localPath];
+                    if (image) {
+                        [[EaseMessageReadManager defaultManager] showBrowserWithImages:@[image]];
+                        return;
+                    }
+                }
+            }
+            
+            [weakSelf showHudInView:weakSelf.view hint:NSEaseLocalizedString(@"message.downloadingImage", @"downloading a image...")];
+
+            void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
+                [weakSelf hideHud];
+                if (!error) {
+                    //send the acknowledgement
+                    [weakSelf _sendHasReadResponseForMessages:@[model.message] isRead:YES];
+                    NSString *localPath = aMessage == nil ? model.fileLocalPath : [(EMImageMessageBody*)aMessage.body localPath];
+                    if (localPath && localPath.length > 0) {
+                        UIImage *image = [UIImage imageWithContentsOfFile:localPath];
+//                        weakSelf.isScrollToBottom = NO;
+                        if (image)
+                        {
+                            [[EaseMessageReadManager defaultManager] showBrowserWithImages:@[image]];
+                        }
+                        else
+                        {
+                            NSLog(@"Read %@ failed!", localPath);
+                        }
+                        return ;
+                    }
+                }
+                [weakSelf showHint:NSEaseLocalizedString(@"message.imageFail", @"image for failure!")];
+            };
+            
+            if (isCustomDownload) {
+                [self _customDownloadMessageFile:model.message];
+            } else {
+                [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:completion];
+            }
+        }else{
+            //get the message thumbnail
+            if (isCustomDownload) {
+                [self _customDownloadMessageFile:model.message];
+            } else {
+                [[EMClient sharedClient].chatManager downloadMessageThumbnail:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
+                    if (!error) {
+                        [weakSelf _reloadTableViewDataWithMessage:model.message];
+                    }else{
+                        [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
+                    }
+                }];
+            }
+        }
+    }
+}
+
+/*!
+ @method
+ @brief ���������������������������
+ @discussion
+ @param model ������model
+ @result
+ */
+- (void)_audioMessageCellSelected:(id<IMessageModel>)model
+{
+    _scrollToBottomWhenAppear = NO;
+    EMVoiceMessageBody *body = (EMVoiceMessageBody*)model.message.body;
+    EMDownloadStatus downloadStatus = [body downloadStatus];
+    if (downloadStatus == EMDownloadStatusDownloading) {
+        [self showHint:NSEaseLocalizedString(@"message.downloadingAudio", @"downloading voice, click later")];
+        return;
+    }
+    else if (downloadStatus == EMDownloadStatusFailed || downloadStatus == EMDownloadStatusPending)
+    {
+        [self showHint:NSEaseLocalizedString(@"message.downloadingAudio", @"downloading voice, click later")];
+        BOOL isCustomDownload = !([EMClient sharedClient].options.isAutoTransferMessageAttachments);
+        if (isCustomDownload) {
+            [self _customDownloadMessageFile:model.message];
+        } else {
+            [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:nil];
+        }
+        
+        return;
+    }
+    
+    // play the audio
+    if (model.bodyType == EMMessageBodyTypeVoice) {
+        //send the acknowledgement
+        [self _sendHasReadResponseForMessages:@[model.message] isRead:YES];
+        __weak EaseMessageViewController *weakSelf = self;
+        BOOL isPrepare = [[EaseMessageReadManager defaultManager] prepareMessageAudioModel:model updateViewCompletion:^(EaseMessageModel *prevAudioModel, EaseMessageModel *currentAudioModel) {
+            if (prevAudioModel || currentAudioModel) {
+                [weakSelf.tableView reloadData];
+            }
+        }];
+        
+        if (isPrepare) {
+            _isPlayingAudio = YES;
+            __weak EaseMessageViewController *weakSelf = self;
+            [[EMCDDeviceManager sharedInstance] enableProximitySensor];
+            [[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:model.fileLocalPath completion:^(NSError *error) {
+                [[EaseMessageReadManager defaultManager] stopMessageAudioModel];
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [weakSelf.tableView reloadData];
+                    weakSelf.isPlayingAudio = NO;
+                    [[EMCDDeviceManager sharedInstance] disableProximitySensor];
+                });
+            }];
+        }
+        else{
+            _isPlayingAudio = NO;
+        }
+    }
+}
+
+#pragma mark - pivate data
+
+/*!
+ @method
+ @brief ������������������
+ @discussion
+ @param messageId ���������������ID
+ @param count     ������������
+ @param isAppend  ���������dataArray������������
+ @result
+ */
+- (void)_loadMessagesBefore:(NSString*)messageId
+                      count:(NSInteger)count
+                     append:(BOOL)isAppend
+{
+    __weak typeof(self) weakSelf = self;
+    void (^refresh)(NSArray *messages) = ^(NSArray *messages) {
+        dispatch_async(_messageQueue, ^{
+            //Format the message
+            NSArray *formattedMessages = [weakSelf formatMessages:messages];
+            
+            //Refresh the page
+            dispatch_async(dispatch_get_main_queue(), ^{
+                EaseMessageViewController *strongSelf = weakSelf;
+                if (strongSelf) {
+                    NSInteger scrollToIndex = 0;
+                    if (isAppend) {
+                        [strongSelf.messsagesSource insertObjects:messages atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [messages count])]];
+                        
+                        //Combine the message
+                        id object = [strongSelf.dataArray firstObject];
+                        if ([object isKindOfClass:[NSString class]]) {
+                            NSString *timestamp = object;
+                            [formattedMessages enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id model, NSUInteger idx, BOOL *stop) {
+                                if ([model isKindOfClass:[NSString class]] && [timestamp isEqualToString:model]) {
+                                    [strongSelf.dataArray removeObjectAtIndex:0];
+                                    *stop = YES;
+                                }
+                            }];
+                        }
+                        scrollToIndex = [strongSelf.dataArray count];
+                        [strongSelf.dataArray insertObjects:formattedMessages atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [formattedMessages count])]];
+                    }
+                    else {
+                        [strongSelf.messsagesSource removeAllObjects];
+                        [strongSelf.messsagesSource addObjectsFromArray:messages];
+                        
+                        [strongSelf.dataArray removeAllObjects];
+                        [strongSelf.dataArray addObjectsFromArray:formattedMessages];
+                    }
+                    
+                    EMMessage *latest = [strongSelf.messsagesSource lastObject];
+                    strongSelf.messageTimeIntervalTag = latest.timestamp;
+                    
+                    [strongSelf.tableView reloadData];
+                    
+                    [strongSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - scrollToIndex - 1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
+                }
+            });
+            
+            //re-download all messages that are not successfully downloaded
+            for (EMMessage *message in messages)
+            {
+                [weakSelf _downloadMessageAttachments:message];
+            }
+            
+            //send the read acknoledgement
+            [weakSelf _sendHasReadResponseForMessages:messages
+                                               isRead:NO];
+        });
+    };
+    
+    [self.conversation loadMessagesStartFromId:messageId count:(int)count searchDirection:EMMessageSearchDirectionUp completion:^(NSArray *aMessages, EMError *aError) {
+        if (!aError && [aMessages count]) {
+            refresh(aMessages);
+        }
+    }];
+}
+
+#pragma mark - GestureRecognizer
+
+-(void)keyBoardHidden:(UITapGestureRecognizer *)tapRecognizer
+{
+    if (tapRecognizer.state == UIGestureRecognizerStateEnded) {
+        [self.chatToolbar endEditing:YES];
+    }
+}
+
+- (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer
+{
+    if (recognizer.state == UIGestureRecognizerStateBegan && [self.dataArray count] > 0)
+    {
+        CGPoint location = [recognizer locationInView:self.tableView];
+        NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint:location];
+        BOOL canLongPress = NO;
+        if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:canLongPressRowAtIndexPath:)]) {
+            canLongPress = [_dataSource messageViewController:self
+                                   canLongPressRowAtIndexPath:indexPath];
+        }
+        
+        if (!canLongPress) {
+            return;
+        }
+        
+        if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:didLongPressRowAtIndexPath:)]) {
+            [_dataSource messageViewController:self
+                    didLongPressRowAtIndexPath:indexPath];
+        }
+        else{
+            id object = [self.dataArray objectAtIndex:indexPath.row];
+            if (![object isKindOfClass:[NSString class]]) {
+                EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
+                [cell becomeFirstResponder];
+                _menuIndexPath = indexPath;
+                [self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
+            }
+        }
+    }
+}
+
+#pragma mark - Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+    // Return the number of sections.
+    return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+    // Return the number of rows in the section.
+    return [self.dataArray count];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    id object = [self.dataArray objectAtIndex:indexPath.row];
+    
+    //time cell
+    if ([object isKindOfClass:[NSString class]]) {
+        NSString *TimeCellIdentifier = [EaseMessageTimeCell cellIdentifier];
+        EaseMessageTimeCell *timeCell = (EaseMessageTimeCell *)[tableView dequeueReusableCellWithIdentifier:TimeCellIdentifier];
+        
+        if (timeCell == nil) {
+            timeCell = [[EaseMessageTimeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TimeCellIdentifier];
+            timeCell.selectionStyle = UITableViewCellSelectionStyleNone;
+        }
+        
+        timeCell.title = object;
+        return timeCell;
+    }
+    else{
+        id<IMessageModel> model = object;
+        if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:cellForMessageModel:)]) {
+            UITableViewCell *cell = [_delegate messageViewController:tableView cellForMessageModel:model];
+            if (cell) {
+                if ([cell isKindOfClass:[EaseMessageCell class]]) {
+                    EaseMessageCell *emcell= (EaseMessageCell*)cell;
+                    if (emcell.delegate == nil) {
+                        emcell.delegate = self;
+                    }
+                }
+                return cell;
+            }
+        }
+        
+        if (_dataSource && [_dataSource respondsToSelector:@selector(isEmotionMessageFormessageViewController:messageModel:)]) {
+            BOOL flag = [_dataSource isEmotionMessageFormessageViewController:self messageModel:model];
+            if (flag) {
+                NSString *CellIdentifier = [EaseCustomMessageCell cellIdentifierWithModel:model];
+                //send cell
+                EaseCustomMessageCell *sendCell = (EaseCustomMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+                
+                // Configure the cell...
+                if (sendCell == nil) {
+                    sendCell = [[EaseCustomMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
+                    sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
+                }
+                
+                if (_dataSource && [_dataSource respondsToSelector:@selector(emotionURLFormessageViewController:messageModel:)]) {
+                    EaseEmotion *emotion = [_dataSource emotionURLFormessageViewController:self messageModel:model];
+                    if (emotion) {
+                        model.image = [UIImage sd_animatedGIFNamed:emotion.emotionOriginal];
+                        model.fileURLPath = emotion.emotionOriginalURL;
+                    }
+                }
+                sendCell.model = model;
+                sendCell.delegate = self;
+                return sendCell;
+            }
+        }
+        
+        NSString *CellIdentifier = [EaseMessageCell cellIdentifierWithModel:model];
+        
+        EaseBaseMessageCell *sendCell = (EaseBaseMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+        
+        // Configure the cell...
+        if (sendCell == nil) {
+            sendCell = [[EaseBaseMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
+            sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
+            sendCell.delegate = self;
+        }
+        
+        sendCell.model = model;
+        return sendCell;
+    }
+}
+
+#pragma mark - Table view delegate
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+    id object = [self.dataArray objectAtIndex:indexPath.row];
+    if ([object isKindOfClass:[NSString class]]) {
+        return self.timeCellHeight;
+    }
+    else{
+        id<IMessageModel> model = object;
+        if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:heightForMessageModel:withCellWidth:)]) {
+            CGFloat height = [_delegate messageViewController:self heightForMessageModel:model withCellWidth:tableView.frame.size.width];
+            if (height) {
+                return height;
+            }
+        }
+        
+        if (_dataSource && [_dataSource respondsToSelector:@selector(isEmotionMessageFormessageViewController:messageModel:)]) {
+            BOOL flag = [_dataSource isEmotionMessageFormessageViewController:self messageModel:model];
+            if (flag) {
+                return [EaseCustomMessageCell cellHeightWithModel:model];
+            }
+        }
+        
+        return [EaseBaseMessageCell cellHeightWithModel:model];
+    }
+}
+
+#pragma mark - UIImagePickerControllerDelegate
+
+- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
+{
+    NSString *mediaType = info[UIImagePickerControllerMediaType];
+    if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
+        NSURL *videoURL = info[UIImagePickerControllerMediaURL];
+        // video url:
+        // file:///private/var/mobile/Applications/B3CDD0B2-2F19-432B-9CFA-158700F4DE8F/tmp/capture-T0x16e39100.tmp.9R8weF/capturedvideo.mp4
+        // we will convert it to mp4 format
+        NSURL *mp4 = [self _convert2Mp4:videoURL];
+        NSFileManager *fileman = [NSFileManager defaultManager];
+        if ([fileman fileExistsAtPath:videoURL.path]) {
+            NSError *error = nil;
+            [fileman removeItemAtURL:videoURL error:&error];
+            if (error) {
+                NSLog(@"failed to remove file, error:%@.", error);
+            }
+        }
+        [self sendVideoMessageWithURL:mp4];
+        
+    }else{
+        
+        NSURL *url = info[UIImagePickerControllerReferenceURL];
+        if (url == nil) {
+            UIImage *orgImage = info[UIImagePickerControllerOriginalImage];
+            [self sendImageMessage:orgImage];
+        } else {
+            if ([[UIDevice currentDevice].systemVersion doubleValue] >= 9.0f) {
+                PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];
+                [result enumerateObjectsUsingBlock:^(PHAsset *asset , NSUInteger idx, BOOL *stop){
+                    if (asset) {
+                        [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *data, NSString *uti, UIImageOrientation orientation, NSDictionary *dic){
+                            if (data != nil) {
+                                [self sendImageMessageWithData:data];
+                            } else {
+                                [self showHint:NSEaseLocalizedString(@"message.smallerImage", @"The image size is too large, please choose another one")];
+                            }
+                        }];
+                    }
+                }];
+            } else {
+                ALAssetsLibrary *alasset = [[ALAssetsLibrary alloc] init];
+                [alasset assetForURL:url resultBlock:^(ALAsset *asset) {
+                    if (asset) {
+                        ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+                        Byte* buffer = (Byte*)malloc((size_t)[assetRepresentation size]);
+                        NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil];
+                        NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+                        [self sendImageMessageWithData:fileData];
+                    }
+                } failureBlock:NULL];
+            }
+        }
+    }
+    
+    [picker dismissViewControllerAnimated:YES completion:nil];
+    
+    self.isViewDidAppear = YES;
+    [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
+}
+
+- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
+{
+    [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
+    
+    self.isViewDidAppear = YES;
+    [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
+}
+
+#pragma mark - EaseMessageCellDelegate
+
+- (void)messageCellSelected:(id<IMessageModel>)model
+{
+    if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didSelectMessageModel:)]) {
+        BOOL flag = [_delegate messageViewController:self didSelectMessageModel:model];
+        if (flag) {
+            [self _sendHasReadResponseForMessages:@[model.message] isRead:YES];
+            return;
+        }
+    }
+    
+    switch (model.bodyType) {
+        case EMMessageBodyTypeImage:
+        {
+            _scrollToBottomWhenAppear = NO;
+            [self _imageMessageCellSelected:model];
+        }
+            break;
+        case EMMessageBodyTypeLocation:
+        {
+            [self _locationMessageCellSelected:model];
+        }
+            break;
+        case EMMessageBodyTypeVoice:
+        {
+            [self _audioMessageCellSelected:model];
+        }
+            break;
+        case EMMessageBodyTypeVideo:
+        {
+            [self _videoMessageCellSelected:model];
+            
+        }
+            break;
+        case EMMessageBodyTypeFile:
+        {
+            _scrollToBottomWhenAppear = NO;
+            [self showHint:@"Custom implementation!"];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)statusButtonSelcted:(id<IMessageModel>)model withMessageCell:(EaseMessageCell*)messageCell
+{
+    if ((model.messageStatus != EMMessageStatusFailed) && (model.messageStatus != EMMessageStatusPending))
+    {
+        return;
+    }
+    
+    __weak typeof(self) weakself = self;
+    [[[EMClient sharedClient] chatManager] resendMessage:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
+        if (!error) {
+            [weakself _refreshAfterSentMessage:message];
+        }
+        else {
+            [weakself.tableView reloadData];
+        }
+    }];
+    
+    [self.tableView reloadData];
+}
+
+- (void)avatarViewSelcted:(id<IMessageModel>)model
+{
+    if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didSelectAvatarMessageModel:)]) {
+        [_delegate messageViewController:self didSelectAvatarMessageModel:model];
+        
+        return;
+    }
+    
+    _scrollToBottomWhenAppear = NO;
+}
+
+#pragma mark - EMChatToolbarDelegate
+
+- (void)chatToolbarDidChangeFrameToHeight:(CGFloat)toHeight
+{
+    [UIView animateWithDuration:0.3 animations:^{
+        CGRect rect = self.tableView.frame;
+        rect.origin.y = 0;
+        rect.size.height = self.view.frame.size.height - toHeight - iPhoneX_BOTTOM_HEIGHT;
+        self.tableView.frame = rect;
+    }];
+    
+    [self _scrollViewToBottom:NO];
+}
+
+- (void)inputTextViewWillBeginEditing:(EaseTextView *)inputTextView
+{
+    if (_menuController == nil) {
+        _menuController = [UIMenuController sharedMenuController];
+    }
+    [_menuController setMenuItems:nil];
+}
+
+- (void)didSendText:(NSString *)text
+{
+    if (text && text.length > 0) {
+        [self sendTextMessage:text];
+        [self.atTargets removeAllObjects];
+    }
+}
+
+- (BOOL)didInputAtInLocation:(NSUInteger)location
+{
+    if ([self.delegate respondsToSelector:@selector(messageViewController:selectAtTarget:)] && self.conversation.type == EMConversationTypeGroupChat) {
+        location += 1;
+        __weak typeof(self) weakSelf = self;
+        [self.delegate messageViewController:self selectAtTarget:^(EaseAtTarget *target) {
+            __strong EaseMessageViewController *strongSelf = weakSelf;
+            if (strongSelf && target) {
+                if ([target.userId length] || [target.nickname length]) {
+                    [strongSelf.atTargets addObject:target];
+                    NSString *insertStr = [NSString stringWithFormat:@"%@ ", target.nickname ? target.nickname : target.userId];
+                    EaseChatToolbar *toolbar = (EaseChatToolbar*)strongSelf.chatToolbar;
+                    NSMutableString *originStr = [toolbar.inputTextView.text mutableCopy];
+                    NSUInteger insertLocation = location > originStr.length ? originStr.length : location;
+                    [originStr insertString:insertStr atIndex:insertLocation];
+                    toolbar.inputTextView.text = originStr;
+                    toolbar.inputTextView.selectedRange = NSMakeRange(insertLocation + insertStr.length, 0);
+                    [toolbar.inputTextView becomeFirstResponder];
+                }
+            }
+            else if (strongSelf) {
+                EaseChatToolbar *toolbar = (EaseChatToolbar*)strongSelf.chatToolbar;
+                [toolbar.inputTextView becomeFirstResponder];
+            }
+        }];
+        EaseChatToolbar *toolbar = (EaseChatToolbar*)self.chatToolbar;
+        toolbar.inputTextView.text = [NSString stringWithFormat:@"%@@", toolbar.inputTextView.text];
+        [toolbar.inputTextView resignFirstResponder];
+        return YES;
+    }
+    else {
+        return NO;
+    }
+}
+
+- (BOOL)didDeleteCharacterFromLocation:(NSUInteger)location
+{
+    EaseChatToolbar *toolbar = (EaseChatToolbar*)self.chatToolbar;
+    if ([toolbar.inputTextView.text length] == location + 1) {
+        //delete last character
+        NSString *inputText = toolbar.inputTextView.text;
+        NSRange range = [inputText rangeOfString:@"@" options:NSBackwardsSearch];
+        if (range.location != NSNotFound) {
+            if (location - range.location > 1) {
+                NSString *sub = [inputText substringWithRange:NSMakeRange(range.location + 1, location - range.location - 1)];
+                for (EaseAtTarget *target in self.atTargets) {
+                    if ([sub isEqualToString:target.userId] || [sub isEqualToString:target.nickname]) {
+                        inputText = range.location > 0 ? [inputText substringToIndex:range.location] : @"";
+                        toolbar.inputTextView.text = inputText;
+                        toolbar.inputTextView.selectedRange = NSMakeRange(inputText.length, 0);
+                        [self.atTargets removeObject:target];
+                        return YES;
+                    }
+                }
+            }
+        }
+    }
+    return NO;
+}
+
+- (void)didSendText:(NSString *)text withExt:(NSDictionary*)ext
+{
+    if ([ext objectForKey:EASEUI_EMOTION_DEFAULT_EXT]) {
+        EaseEmotion *emotion = [ext objectForKey:EASEUI_EMOTION_DEFAULT_EXT];
+        if (self.dataSource && [self.dataSource respondsToSelector:@selector(emotionExtFormessageViewController:easeEmotion:)]) {
+            NSDictionary *ext = [self.dataSource emotionExtFormessageViewController:self easeEmotion:emotion];
+            [self sendTextMessage:emotion.emotionTitle withExt:ext];
+        } else {
+            [self sendTextMessage:emotion.emotionTitle withExt:@{MESSAGE_ATTR_EXPRESSION_ID:emotion.emotionId,MESSAGE_ATTR_IS_BIG_EXPRESSION:@(YES)}];
+        }
+        return;
+    }
+    if (text && text.length > 0) {
+        [self sendTextMessage:text withExt:ext];
+    }
+}
+
+- (void)didStartRecordingVoiceAction:(UIView *)recordView
+{
+    if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
+        [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchDown];
+    } else {
+        if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
+            [(EaseRecordView *)self.recordView recordButtonTouchDown];
+        }
+    }
+    
+    [self _canRecordCompletion:^(EMRecordResponse recordResponse) {
+        switch (recordResponse) {
+            case EMRequestRecord:
+                
+                break;
+            case EMCanRecord:
+            {
+                _isRecording = YES;
+                EaseRecordView *tmpView = (EaseRecordView *)recordView;
+                tmpView.center = self.view.center;
+                [self.view addSubview:tmpView];
+                [self.view bringSubviewToFront:recordView];
+                int x = arc4random() % 100000;
+                NSTimeInterval time = [[NSDate date] timeIntervalSince1970];
+                NSString *fileName = [NSString stringWithFormat:@"%d%d",(int)time,x];
+                
+                [[EMCDDeviceManager sharedInstance] asyncStartRecordingWithFileName:fileName completion:^(NSError *error)
+                 {
+                     if (error) {
+                         NSLog(@"%@",NSEaseLocalizedString(@"message.startRecordFail", @"failure to start recording"));
+                         _isRecording = NO;
+                     }
+                 }];
+                
+            }
+                break;
+            case EMCanNotRecord:
+            {
+                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"record.failToPermission", @"No recording permission") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil];
+                [alertView show];
+            }
+                break;
+            default:
+                break;
+        }
+    }];
+}
+
+
+- (void)didCancelRecordingVoiceAction:(UIView *)recordView
+{
+    if(_isRecording) {
+        [[EMCDDeviceManager sharedInstance] cancelCurrentRecording];
+        if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
+            [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchUpOutside];
+        } else {
+            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
+                [(EaseRecordView *)self.recordView recordButtonTouchUpOutside];
+            }
+            [self.recordView removeFromSuperview];
+        }
+        
+        _isRecording = NO;
+    }
+}
+
+- (void)didFinishRecoingVoiceAction:(UIView *)recordView
+{
+    if (_isRecording) {
+        if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
+            [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchUpInside];
+        } else {
+            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
+                [(EaseRecordView *)self.recordView recordButtonTouchUpInside];
+            }
+            [self.recordView removeFromSuperview];
+        }
+        __weak typeof(self) weakSelf = self;
+        [[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
+            if (!error) {
+                [weakSelf sendVoiceMessageWithLocalPath:recordPath duration:aDuration];
+            }
+            else {
+                [weakSelf showHudInView:self.view hint:error.domain];
+                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+                    [weakSelf hideHud];
+                });
+            }
+        }];
+        _isRecording = NO;
+    }
+}
+
+- (void)didDragInsideAction:(UIView *)recordView
+{
+    if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
+        [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeDragInside];
+    } else {
+        if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
+            [(EaseRecordView *)self.recordView recordButtonDragInside];
+        }
+    }
+}
+
+- (void)didDragOutsideAction:(UIView *)recordView
+{
+    if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
+        [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeDragOutside];
+    } else {
+        if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
+            [(EaseRecordView *)self.recordView recordButtonDragOutside];
+        }
+    }
+}
+
+#pragma mark - EaseChatBarMoreViewDelegate
+
+- (void)moreView:(EaseChatBarMoreView *)moreView didItemInMoreViewAtIndex:(NSInteger)index
+{
+    if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectMoreView:AtIndex:)]) {
+        [self.delegate messageViewController:self didSelectMoreView:moreView AtIndex:index];
+        return;
+    }
+}
+
+- (void)moreViewPhotoAction:(EaseChatBarMoreView *)moreView
+{
+    // Hide the keyboard
+    [self.chatToolbar endEditing:YES];
+    
+    // Pop image picker
+    self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
+    self.imagePicker.mediaTypes = @[(NSString *)kUTTypeImage];
+    [self presentViewController:self.imagePicker animated:YES completion:NULL];
+    
+    self.isViewDidAppear = NO;
+    [[EaseSDKHelper shareHelper] setIsShowingimagePicker:YES];
+}
+
+- (void)moreViewTakePicAction:(EaseChatBarMoreView *)moreView
+{
+    // Hide the keyboard
+    [self.chatToolbar endEditing:YES];
+    
+#if TARGET_IPHONE_SIMULATOR
+    [self showHint:NSEaseLocalizedString(@"message.simulatorNotSupportCamera", @"simulator does not support taking picture")];
+#elif TARGET_OS_IPHONE
+    self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
+    self.imagePicker.mediaTypes = @[(NSString *)kUTTypeImage,(NSString *)kUTTypeMovie];
+    [self presentViewController:self.imagePicker animated:YES completion:NULL];
+    
+    self.isViewDidAppear = NO;
+    [[EaseSDKHelper shareHelper] setIsShowingimagePicker:YES];
+#endif
+}
+
+- (void)moreViewLocationAction:(EaseChatBarMoreView *)moreView
+{
+    // Hide the keyboard
+    [self.chatToolbar endEditing:YES];
+    
+    EaseLocationViewController *locationController = [[EaseLocationViewController alloc] init];
+    locationController.delegate = self;
+    [self.navigationController pushViewController:locationController animated:YES];
+}
+
+- (void)moreViewAudioCallAction:(EaseChatBarMoreView *)moreView
+{
+    // Hide the keyboard
+    [self.chatToolbar endEditing:YES];
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_CALL object:@{@"chatter":self.conversation.conversationId, @"type":[NSNumber numberWithInt:0]}];
+}
+
+- (void)moreViewVideoCallAction:(EaseChatBarMoreView *)moreView
+{
+    // Hide the keyboard
+    [self.chatToolbar endEditing:YES];
+    
+    [[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_CALL object:@{@"chatter":self.conversation.conversationId, @"type":[NSNumber numberWithInt:1]}];
+}
+
+#pragma mark - EMLocationViewDelegate
+
+-(void)sendLocationLatitude:(double)latitude
+                  longitude:(double)longitude
+                 andAddress:(NSString *)address
+{
+    [self sendLocationMessageLatitude:latitude longitude:longitude andAddress:address];
+}
+
+#pragma mark - Hyphenate
+
+#pragma mark - EMChatManagerDelegate
+
+- (void)didReceiveMessages:(NSArray *)aMessages
+{
+    for (EMMessage *message in aMessages) {
+        if ([self.conversation.conversationId isEqualToString:message.conversationId]) {
+            [self addMessageToDataSource:message progress:nil];
+            
+            [self _sendHasReadResponseForMessages:@[message]
+                                           isRead:NO];
+            
+            if ([self _shouldMarkMessageAsRead])
+            {
+                [self.conversation markMessageAsReadWithId:message.messageId error:nil];
+            }
+        }
+    }
+}
+
+- (void)didReceiveCmdMessages:(NSArray *)aCmdMessages
+{
+    for (EMMessage *message in aCmdMessages) {
+        if ([self.conversation.conversationId isEqualToString:message.conversationId]) {
+            [self showHint:NSEaseLocalizedString(@"receiveCmd", @"receive cmd message")];
+            break;
+        }
+    }
+}
+
+- (void)didReceiveHasDeliveredAcks:(NSArray *)aMessages
+{
+    for(EMMessage *message in aMessages){
+        [self _updateMessageStatus:message];
+    }
+}
+
+- (void)didReceiveHasReadAcks:(NSArray *)aMessages
+{
+    for (EMMessage *message in aMessages) {
+        if (![self.conversation.conversationId isEqualToString:message.conversationId]){
+            continue;
+        }
+        
+        __block id<IMessageModel> model = nil;
+        __block BOOL isHave = NO;
+        [self.dataArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
+         {
+             if ([obj conformsToProtocol:@protocol(IMessageModel)])
+             {
+                 model = (id<IMessageModel>)obj;
+                 if ([model.messageId isEqualToString:message.messageId])
+                 {
+                     model.message.isReadAcked = YES;
+                     isHave = YES;
+                     *stop = YES;
+                 }
+             }
+         }];
+        
+        if(!isHave){
+            return;
+        }
+        
+        if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didReceiveHasReadAckForModel:)]) {
+            [_delegate messageViewController:self didReceiveHasReadAckForModel:model];
+        }
+        else{
+            [self.tableView reloadData];
+        }
+    }
+}
+
+- (void)didMessageStatusChanged:(EMMessage *)aMessage
+                          error:(EMError *)aError;
+{
+    [self _updateMessageStatus:aMessage];
+}
+
+- (void)didMessageAttachmentsStatusChanged:(EMMessage *)message
+                                     error:(EMError *)error{
+    if (!error) {
+        EMFileMessageBody *fileBody = (EMFileMessageBody*)[message body];
+        if ([fileBody type] == EMMessageBodyTypeImage) {
+            EMImageMessageBody *imageBody = (EMImageMessageBody *)fileBody;
+            if ([imageBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
+            {
+                [self _reloadTableViewDataWithMessage:message];
+            }
+        }else if([fileBody type] == EMMessageBodyTypeVideo){
+            EMVideoMessageBody *videoBody = (EMVideoMessageBody *)fileBody;
+            if ([videoBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
+            {
+                [self _reloadTableViewDataWithMessage:message];
+            }
+        }else if([fileBody type] == EMMessageBodyTypeVoice){
+            if ([fileBody downloadStatus] == EMDownloadStatusSuccessed)
+            {
+                [self _reloadTableViewDataWithMessage:message];
+            }
+        }
+        
+    }else{
+        
+    }
+}
+
+#pragma mark - EMCDDeviceManagerProximitySensorDelegate
+
+- (void)proximitySensorChanged:(BOOL)isCloseToUser
+{
+    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
+    if (isCloseToUser)
+    {
+        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
+    } else {
+        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
+        if (self.playingVoiceModel == nil) {
+            [[EMCDDeviceManager sharedInstance] disableProximitySensor];
+        }
+    }
+    [audioSession setActive:YES error:nil];
+}
+
+#pragma mark - action
+
+- (void)copyMenuAction:(id)sender
+{
+    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+    if (self.menuIndexPath && self.menuIndexPath.row > 0) {
+        id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
+        pasteboard.string = model.text;
+    }
+    
+    self.menuIndexPath = nil;
+}
+
+- (void)deleteMenuAction:(id)sender
+{
+    if (self.menuIndexPath && self.menuIndexPath.row > 0) {
+        id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
+        NSMutableIndexSet *indexs = [NSMutableIndexSet indexSetWithIndex:self.menuIndexPath.row];
+        NSMutableArray *indexPaths = [NSMutableArray arrayWithObjects:self.menuIndexPath, nil];
+        
+        [self.conversation deleteMessageWithId:model.message.messageId error:nil];
+        [self.messsagesSource removeObject:model.message];
+        
+        if (self.menuIndexPath.row - 1 >= 0) {
+            id nextMessage = nil;
+            id prevMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row - 1)];
+            if (self.menuIndexPath.row + 1 < [self.dataArray count]) {
+                nextMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row + 1)];
+            }
+            if ((!nextMessage || [nextMessage isKindOfClass:[NSString class]]) && [prevMessage isKindOfClass:[NSString class]]) {
+                [indexs addIndex:self.menuIndexPath.row - 1];
+                [indexPaths addObject:[NSIndexPath indexPathForRow:(self.menuIndexPath.row - 1) inSection:0]];
+            }
+        }
+        
+        [self.dataArray removeObjectsAtIndexes:indexs];
+        [self.tableView beginUpdates];
+        [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
+        [self.tableView endUpdates];
+    }
+    
+    self.menuIndexPath = nil;
+}
+
+#pragma mark - public
+
+- (NSArray *)formatMessages:(NSArray *)messages
+{
+    NSMutableArray *formattedArray = [[NSMutableArray alloc] init];
+    if ([messages count] == 0) {
+        return formattedArray;
+    }
+    
+    for (EMMessage *message in messages) {
+        //Calculate time interval
+        CGFloat interval = (self.messageTimeIntervalTag - message.timestamp) / 1000;
+        if (self.messageTimeIntervalTag < 0 || interval > 60 || interval < -60) {
+            NSDate *messageDate = [NSDate dateWithTimeIntervalInMilliSecondSince1970:(NSTimeInterval)message.timestamp];
+            NSString *timeStr = @"";
+            
+            if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:stringForDate:)]) {
+                timeStr = [_dataSource messageViewController:self stringForDate:messageDate];
+            }
+            else{
+                timeStr = [messageDate formattedTime];
+            }
+            [formattedArray addObject:timeStr];
+            self.messageTimeIntervalTag = message.timestamp;
+        }
+        
+        //Construct message model
+        id<IMessageModel> model = nil;
+        if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
+            model = [_dataSource messageViewController:self modelForMessage:message];
+        }
+        else{
+            model = [[EaseMessageModel alloc] initWithMessage:message];
+            model.avatarImage = [UIImage easeImageNamed:@"EaseUIResource.bundle/user"];
+            model.failImageName = @"imageDownloadFail";
+        }
+        
+        if (model) {
+            [formattedArray addObject:model];
+        }
+    }
+    
+    return formattedArray;
+}
+
+-(void)addMessageToDataSource:(EMMessage *)message
+                     progress:(id)progress
+{
+    [self.messsagesSource addObject:message];
+    
+    __weak EaseMessageViewController *weakSelf = self;
+    dispatch_async(_messageQueue, ^{
+        NSArray *messages = [weakSelf formatMessages:@[message]];
+        
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [weakSelf.dataArray addObjectsFromArray:messages];
+            [weakSelf.tableView reloadData];
+            [weakSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[weakSelf.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
+        });
+    });
+}
+
+#pragma mark - public
+- (void)tableViewDidTriggerHeaderRefresh
+{
+    self.messageTimeIntervalTag = -1;
+    NSString *messageId = nil;
+    if ([self.messsagesSource count] > 0) {
+        messageId = [(EMMessage *)self.messsagesSource.firstObject messageId];
+    }
+    else {
+        messageId = nil;
+    }
+    [self _loadMessagesBefore:messageId count:self.messageCountOfPage append:YES];
+    
+    [self tableViewDidFinishTriggerHeader:YES reload:YES];
+}
+
+#pragma mark - send message
+
+- (void)_refreshAfterSentMessage:(EMMessage*)aMessage
+{
+    if ([self.messsagesSource count] && [EMClient sharedClient].options.sortMessageByServerTime) {
+        NSString *msgId = aMessage.messageId;
+        EMMessage *last = self.messsagesSource.lastObject;
+        if ([last isKindOfClass:[EMMessage class]]) {
+            
+            __block NSUInteger index = NSNotFound;
+            index = NSNotFound;
+            [self.messsagesSource enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(EMMessage *obj, NSUInteger idx, BOOL *stop) {
+                if ([obj isKindOfClass:[EMMessage class]] && [obj.messageId isEqualToString:msgId]) {
+                    index = idx;
+                    *stop = YES;
+                }
+            }];
+            if (index != NSNotFound) {
+                [self.messsagesSource removeObjectAtIndex:index];
+                [self.messsagesSource addObject:aMessage];
+                
+                //���������������
+                self.messageTimeIntervalTag = -1;
+                NSArray *formattedMessages = [self formatMessages:self.messsagesSource];
+                [self.dataArray removeAllObjects];
+                [self.dataArray addObjectsFromArray:formattedMessages];
+                [self.tableView reloadData];
+                [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
+                return;
+            }
+        }
+    }
+    [self.tableView reloadData];
+}
+
+- (void)_sendMessage:(EMMessage *)message
+    isNeedUploadFile:(BOOL)isUploadFile
+{
+    if (self.conversation.type == EMConversationTypeGroupChat){
+        message.chatType = EMChatTypeGroupChat;
+    }
+    else if (self.conversation.type == EMConversationTypeChatRoom){
+        message.chatType = EMChatTypeChatRoom;
+    }
+    
+    __weak typeof(self) weakself = self;
+    if (!([EMClient sharedClient].options.isAutoTransferMessageAttachments) && isUploadFile) {
+        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:NSLocalizedString(@"message.autoTransfer", @"Please customize the transfer attachment method") delegate:nil cancelButtonTitle:NSLocalizedString(@"sure", @"OK") otherButtonTitles:nil, nil];
+        [alertView show];
+    } else {
+        [self addMessageToDataSource:message
+                            progress:nil];
+        
+        [[EMClient sharedClient].chatManager sendMessage:message progress:^(int progress) {
+            if (weakself.dataSource && [weakself.dataSource respondsToSelector:@selector(messageViewController:updateProgress:messageModel:messageBody:)]) {
+                [weakself.dataSource messageViewController:weakself updateProgress:progress messageModel:nil messageBody:message.body];
+            }
+        } completion:^(EMMessage *aMessage, EMError *aError) {
+            if (!aError) {
+                [weakself _refreshAfterSentMessage:aMessage];
+            }
+            else {
+                [weakself.tableView reloadData];
+            }
+        }];
+    }
+}
+
+- (void)sendTextMessage:(NSString *)text
+{
+    NSDictionary *ext = nil;
+    if (self.conversation.type == EMConversationTypeGroupChat) {
+        NSArray *targets = [self _searchAtTargets:text];
+        if ([targets count]) {
+            __block BOOL atAll = NO;
+            [targets enumerateObjectsUsingBlock:^(NSString *target, NSUInteger idx, BOOL *stop) {
+                if ([target compare:kGroupMessageAtAll options:NSCaseInsensitiveSearch] == NSOrderedSame) {
+                    atAll = YES;
+                    *stop = YES;
+                }
+            }];
+            if (atAll) {
+                ext = @{kGroupMessageAtList: kGroupMessageAtAll};
+            }
+            else {
+                ext = @{kGroupMessageAtList: targets};
+            }
+        }
+    }
+    [self sendTextMessage:text withExt:ext];
+}
+
+- (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext
+{
+    EMMessage *message = [EaseSDKHelper getTextMessage:text to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:ext];
+    [self _sendMessage:message isNeedUploadFile:NO];
+}
+
+- (void)sendLocationMessageLatitude:(double)latitude
+                          longitude:(double)longitude
+                         andAddress:(NSString *)address
+{
+    EMMessage *message = [EaseSDKHelper getLocationMessageWithLatitude:latitude longitude:longitude address:address to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:nil];
+    [self _sendMessage:message isNeedUploadFile:NO];
+}
+
+- (void)sendImageMessageWithData:(NSData *)imageData
+{
+    id progress = nil;
+    if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
+        progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
+    }
+    else{
+        progress = self;
+    }
+    
+    EMMessage *message = [EaseSDKHelper getImageMessageWithImageData:imageData to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:nil];
+    [self _sendMessage:message isNeedUploadFile:YES];
+}
+
+- (void)sendImageMessage:(UIImage *)image
+{
+    id progress = nil;
+    if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
+        progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
+    }
+    else{
+        progress = self;
+    }
+    
+    EMMessage *message = [EaseSDKHelper getImageMessageWithImage:image to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:nil];
+    [self _sendMessage:message isNeedUploadFile:YES];
+}
+
+- (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
+                             duration:(NSInteger)duration
+{
+    id progress = nil;
+    if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
+        progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVoice];
+    }
+    else{
+        progress = self;
+    }
+    
+    EMMessage *message = [EaseSDKHelper getVoiceMessageWithLocalPath:localPath duration:duration to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:nil];
+    [self _sendMessage:message isNeedUploadFile:YES];
+}
+
+- (void)sendVideoMessageWithURL:(NSURL *)url
+{
+    id progress = nil;
+    if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
+        progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVideo];
+    }
+    else{
+        progress = self;
+    }
+    
+    EMMessage *message = [EaseSDKHelper getVideoMessageWithURL:url to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:nil];
+    [self _sendMessage:message isNeedUploadFile:YES];
+}
+
+- (void)sendFileMessageWith:(EMMessage *)message {
+    [self _sendMessage:message isNeedUploadFile:YES];
+}
+
+#pragma mark - notifycation
+- (void)didBecomeActive
+{
+    self.messageTimeIntervalTag = -1;
+    self.dataArray = [[self formatMessages:self.messsagesSource] mutableCopy];
+    [self.tableView reloadData];
+    
+    if (self.isViewDidAppear)
+    {
+        NSMutableArray *unreadMessages = [NSMutableArray array];
+        for (EMMessage *message in self.messsagesSource)
+        {
+            if ([self shouldSendHasReadAckForMessage:message read:NO])
+            {
+                [unreadMessages addObject:message];
+            }
+        }
+        if ([unreadMessages count])
+        {
+            [self _sendHasReadResponseForMessages:unreadMessages isRead:YES];
+        }
+        
+        [_conversation markAllMessagesAsRead:nil];
+        if (self.dataSource && [self.dataSource respondsToSelector:@selector(messageViewControllerMarkAllMessagesAsRead:)]) {
+            [self.dataSource messageViewControllerMarkAllMessagesAsRead:self];
+        }
+    }
+}
+
+- (void)hideImagePicker
+{
+    if (_imagePicker && [EaseSDKHelper shareHelper].isShowingimagePicker) {
+        [_imagePicker dismissViewControllerAnimated:NO completion:nil];
+    }
+}
+
+#pragma mark - private
+- (void)_reloadTableViewDataWithMessage:(EMMessage *)message
+{
+    if ([self.conversation.conversationId isEqualToString:message.conversationId])
+    {
+        for (int i = 0; i < self.dataArray.count; i ++) {
+            id object = [self.dataArray objectAtIndex:i];
+            if ([object isKindOfClass:[EaseMessageModel class]]) {
+                id<IMessageModel> model = object;
+                if ([message.messageId isEqualToString:model.messageId]) {
+                    id<IMessageModel> model = nil;
+                    if (self.dataSource && [self.dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
+                        model = [self.dataSource messageViewController:self modelForMessage:message];
+                    }
+                    else{
+                        model = [[EaseMessageModel alloc] initWithMessage:message];
+                        model.avatarImage = [UIImage easeImageNamed:@"EaseUIResource.bundle/user"];
+                        model.failImageName = @"imageDownloadFail";
+                    }
+                    
+                    [self.tableView beginUpdates];
+                    [self.dataArray replaceObjectAtIndex:i withObject:model];
+                    [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
+                    [self.tableView endUpdates];
+                    break;
+                }
+            }
+        }
+    }
+}
+
+- (void)_updateMessageStatus:(EMMessage *)aMessage
+{
+    BOOL isChatting = [aMessage.conversationId isEqualToString:self.conversation.conversationId];
+    if (aMessage && isChatting) {
+        id<IMessageModel> model = nil;
+        if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
+            model = [_dataSource messageViewController:self modelForMessage:aMessage];
+        }
+        else{
+            model = [[EaseMessageModel alloc] initWithMessage:aMessage];
+            model.avatarImage = [UIImage easeImageNamed:@"EaseUIResource.bundle/user"];
+            model.failImageName = @"imageDownloadFail";
+        }
+        if (model) {
+            __block NSUInteger index = NSNotFound;
+            [self.dataArray enumerateObjectsUsingBlock:^(EaseMessageModel *model, NSUInteger idx, BOOL *stop){
+                if ([model conformsToProtocol:@protocol(IMessageModel)]) {
+                    if ([aMessage.messageId isEqualToString:model.message.messageId])
+                    {
+                        index = idx;
+                        *stop = YES;
+                    }
+                }
+            }];
+            
+            if (index != NSNotFound)
+            {
+                [self.dataArray replaceObjectAtIndex:index withObject:model];
+                [self.tableView beginUpdates];
+                [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
+                [self.tableView endUpdates];
+            }
+        }
+    }
+}
+
+- (NSArray*)_searchAtTargets:(NSString*)text
+{
+    NSMutableArray *targets = nil;
+    if (text.length > 1) {
+        targets = [NSMutableArray array];
+        NSArray *splits = [text componentsSeparatedByString:@"@"];
+        if ([splits count]) {
+            for (NSString *split in splits) {
+                if (split.length) {
+                    NSString *atALl = NSEaseLocalizedString(@"group.atAll", @"all");
+                    if (split.length >= atALl.length && [split compare:atALl options:NSCaseInsensitiveSearch range:NSMakeRange(0, atALl.length)] == NSOrderedSame) {
+                        [targets removeAllObjects];
+                        [targets addObject:kGroupMessageAtAll];
+                        return targets;
+                    }
+                    for (EaseAtTarget *target in self.atTargets) {
+                        if ([target.userId length]) {
+                            if ([split hasPrefix:target.userId] || (target.nickname && [split hasPrefix:target.nickname])) {
+                                [targets addObject:target.userId];
+                                [self.atTargets removeObject:target];
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return targets;
+}
+
+
+@end

--
Gitblit v1.8.0