Files
Ash_AWord/Ash_AWord/ViewControllers/Chat/ChatMainViewController.m
T
2015-06-26 15:49:48 +08:00

1239 lines
49 KiB
Objective-C

//
// ChatMainViewController.m
// Ash_AWord
//
// Created by xmfish on 15/6/18.
// Copyright (c) 2015年 ash. All rights reserved.
//
#import "ChatMainViewController.h"
#import "DXMessageToolBar.h"
#import "DXChatBarMoreView.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "EMCDDeviceManager.h"
#import "EMChatVoice.h"
#import "EMChatVideo.h"
#import "LocationViewController.h"
#import "CallViewController.h"
#import "EaseMob.h"
#import "ChatSendHelper.h"
#import "EMChatTimeCell.h"
#import "MessageViewModel.h"
#import "MessageModel.h"
#import "EMChatViewCell.h"
#import "NSDate+Category.h"
#import "MessageModelManager.h"
#import "NSDateFormatter+Category.h"
#import "MessageReadManager.h"
#import "SRRefreshView.h"
#import "PersonalInfoViewController.h"
#import "AppDelegate.h"
#define KPageCount 20
@interface ChatMainViewController ()<UITableViewDelegate,UITableViewDataSource,DXMessageToolBarDelegate,LocationViewDelegate,UINavigationControllerDelegate, UIImagePickerControllerDelegate,DXChatBarMoreViewDelegate,EMCallManagerDelegate,EMCDDeviceManagerDelegate,EMChatManagerDelegate,SRRefreshDelegate>
{
DXMessageToolBar *_chatToolBar;
NSMutableArray* _dataSource;
NSMutableArray *_messages;
dispatch_queue_t _messageQueue;
NSDate *_chatTagDate;
}
@property (strong, nonatomic) EMConversation *conversation;//会话管理者
@property (strong, nonatomic) MessageReadManager *messageReadManager;//message阅读的管理者
@property (strong, nonatomic) SRRefreshView *slimeView;
@property (nonatomic) BOOL isPlayingAudio;
@end
@implementation ChatMainViewController
- (MessageReadManager *)messageReadManager
{
if (_messageReadManager == nil) {
_messageReadManager = [MessageReadManager defaultManager];
}
return _messageReadManager;
}
- (SRRefreshView *)slimeView
{
if (_slimeView == nil) {
_slimeView = [[SRRefreshView alloc] init];
_slimeView.delegate = self;
_slimeView.upInset = 0;
_slimeView.slimeMissWhenGoingBack = YES;
_slimeView.slime.bodyColor = [UIColor grayColor];
_slimeView.slime.skinColor = [UIColor grayColor];
_slimeView.slime.lineWith = 1;
_slimeView.slime.shadowBlur = 4;
_slimeView.slime.shadowColor = [UIColor grayColor];
}
return _slimeView;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self customViewDidLoad];
_tableView.dataSource = self;
_tableView.delegate = self;
_dataSource = [NSMutableArray array];
_messages = [NSMutableArray array];
_messageQueue = dispatch_queue_create("easemob.com", NULL);
_isPlayingAudio = NO;
self.navigationItem.title = _otherUserName;
if (!_otherUserName) {
_otherUserName = @"";
}
if (!_otherUserAvatar) {
_otherUserAvatar = @"";
}
//根据接收者的username获取当前会话的管理者
_conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:_otherUserId conversationType:eConversationTypeChat];
[_conversation markAllMessagesAsRead:YES];
_chatToolBar = [[DXMessageToolBar alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-[DXMessageToolBar defaultHeight], kScreenWidth, [DXMessageToolBar defaultHeight])];
_chatToolBar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
_chatToolBar.delegate = self;
ChatMoreType type = ChatMoreTypeChat;
DXChatBarMoreView* dxChatBarMoreView = [[DXChatBarMoreView alloc] initWithFrame:CGRectMake(0, (kVerticalPadding * 2 + kInputTextViewMinHeight), _chatToolBar.frame.size.width, 80) typw:type];
dxChatBarMoreView.delegate = self;
_chatToolBar.moreView = dxChatBarMoreView;
_chatToolBar.moreView.backgroundColor = [UIColor colorWithRed:240.0/255.0 green:242.0/255.0 blue:247.0/255.0 alpha:1.0];
_chatToolBar.moreView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
[self.view addSubview:_chatToolBar];
#warning 以下三行代码必须写,注册为SDK的ChatManager的delegate
[EMCDDeviceManager sharedInstance].delegate = self;
[[EaseMob sharedInstance].chatManager removeDelegate:self];
//注册为SDK的ChatManager的delegate
[[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
[[EaseMob sharedInstance].callManager removeDelegate:self];
// 注册为Call的Delegate
[[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(keyBoardHidden)];
[self.view addGestureRecognizer:tap];
[self.tableView addSubview:self.slimeView];
//通过会话管理者获取已收发消息
[self loadMoreMessages];
}
#pragma mark - DXMessageToolBarDelegate
- (void)inputTextViewWillBeginEditing:(XHMessageTextView *)messageInputTextView{
}
- (void)didChangeFrameToHeight:(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;
self.tableView.frame = rect;
}];
[self scrollViewToBottom:NO];
}
- (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];
}
}
- (void)didSendText:(NSString *)text
{
if (text && text.length > 0) {
[self sendTextMessage:text];
}
}
/**
* 按下录音按钮开始录音
*/
#pragma mark - private
- (BOOL)canRecord
{
__block BOOL bCanRecord = YES;
if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
[audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
bCanRecord = granted;
}];
}
}
return bCanRecord;
}
- (void)didStartRecordingVoiceAction:(UIView *)recordView
{
if ([self canRecord]) {
DXRecordView *tmpView = (DXRecordView *)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(NSLocalizedString(@"message.startRecordFail", @"failure to start recording"));
}
}];
}
}
/**
* 手指向上滑动取消录音
*/
- (void)didCancelRecordingVoiceAction:(UIView *)recordView
{
[[EMCDDeviceManager sharedInstance] cancelCurrentRecording];
}
/**
* 松开手指完成录音
*/
- (void)didFinishRecoingVoiceAction:(UIView *)recordView
{
[[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
if (!error) {
EMChatVoice *voice = [[EMChatVoice alloc] initWithFile:recordPath
displayName:@"audio"];
voice.duration = aDuration;
[self sendAudioMessage:voice];
}else {
// [weakSelf showHudInView:self.view hint:@"录音时间太短了"];
[MBProgressHUD errorHudWithView:self.view label:@"录音时间太短了" hidesAfter:1.0];
_chatToolBar.recordButton.enabled = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [weakSelf hideHud];
_chatToolBar.recordButton.enabled = YES;
});
}
}];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
NSURL *videoURL = info[UIImagePickerControllerMediaURL];
[picker dismissViewControllerAnimated:YES completion:^{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isShowPicker"];
}];
// 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);
}
}
EMChatVideo *chatVideo = [[EMChatVideo alloc] initWithFile:[mp4 relativePath] displayName:@"video.mp4"];
[self sendVideoMessage:chatVideo];
}else{
UIImage *orgImage = info[UIImagePickerControllerOriginalImage];
[picker dismissViewControllerAnimated:YES completion:^{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isShowPicker"];
}];
[self sendImageMessage:orgImage];
}
// self.isInvisible = NO;
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"isShowPicker"];
[picker dismissViewControllerAnimated:YES completion:nil];
// self.isInvisible = NO;
}
#pragma mark - EMChatBarMoreViewDelegate
- (void)moreViewPhotoAction:(DXChatBarMoreView *)moreView
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"isShowPicker"];
// 隐藏键盘
[self keyBoardHidden];
// 弹出照片选择
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
// picker.allowsEditing = YES;
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:nil];
}
#pragma mark - slimeRefresh delegate
//加载更多
- (void)slimeRefreshStartRefresh:(SRRefreshView *)refreshView
{
_chatTagDate = nil;
[self loadMoreMessages];
[_slimeView endRefresh];
}
// 点击背景隐藏
-(void)keyBoardHidden
{
[_chatToolBar endEditing:YES];
}
- (void)moreViewTakePicAction:(DXChatBarMoreView *)moreView
{
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"isShowPicker"];
[self keyBoardHidden];
#if TARGET_IPHONE_SIMULATOR
// [self showHint:NSLocalizedString(@"message.simulatorNotSupportCamera", @"simulator does not support taking picture")];
#elif TARGET_OS_IPHONE
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
// picker.allowsEditing = YES;
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:nil];
#endif
}
- (void)moreViewLocationAction:(DXChatBarMoreView *)moreView
{
// 隐藏键盘
[self keyBoardHidden];
LocationViewController *locationController = [[LocationViewController alloc] initWithNibName:nil bundle:nil];
locationController.delegate = self;
[self.navigationController pushViewController:locationController animated:YES];
}
- (void)moreViewAudioCallAction:(DXChatBarMoreView *)moreView
{
// 隐藏键盘
[self keyBoardHidden];
[[NSNotificationCenter defaultCenter] postNotificationName:@"callOutWithChatter" object:@{@"chatter":_otherUserId, @"type":[NSNumber numberWithInt:eCallSessionTypeAudio],@"username":_otherUserName,@"useravatar":_otherUserAvatar}];
// EMCallSession *callSession = nil;
// EMError *error = nil;
// callSession = [[EaseMob sharedInstance].callManager asyncMakeVoiceCall:_conversation.chatter timeout:50 error:&error];
//
//
// CallViewController *callController = [[CallViewController alloc] initWithSession:callSession isIncoming:NO withName:_otherUserName withAvatar:_otherUserAvatar];
// callController.modalPresentationStyle = UIModalPresentationOverFullScreen;
// [self presentViewController:callController animated:NO completion:nil];
}
- (void)moreViewVideoCallAction:(DXChatBarMoreView *)moreView
{
// 隐藏键盘
[self keyBoardHidden];
[[NSNotificationCenter defaultCenter] postNotificationName:@"callOutWithChatter" object:@{@"chatter":_otherUserId, @"type":[NSNumber numberWithInt:eCallSessionTypeVideo],@"username":_otherUserName,@"useravatar":_otherUserAvatar}];
// EMCallSession *callSession = nil;
// EMError *error = nil;
//
// if (![CallViewController canVideo]) {
// return;
// }
// callSession = [[EaseMob sharedInstance].callManager asyncMakeVideoCall:_conversation.chatter timeout:50 error:&error];
//
//
// CallViewController *callController = [[CallViewController alloc] initWithSession:callSession isIncoming:NO withName:_otherUserName withAvatar:_otherUserAvatar];
// callController.modalPresentationStyle = UIModalPresentationOverFullScreen;
// [self presentViewController:callController animated:NO completion:nil];
}
#pragma mark - helper
- (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];
mp4Url = [movUrl copy];
mp4Url = [mp4Url URLByDeletingPathExtension];
mp4Url = [mp4Url URLByAppendingPathExtension:@"mp4"];
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;
}
#pragma mark UITableViewDelegate
#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 _dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < [_dataSource count]) {
id obj = [_dataSource objectAtIndex:indexPath.row];
if ([obj isKindOfClass:[NSString class]]) {
EMChatTimeCell *timeCell = (EMChatTimeCell *)[tableView dequeueReusableCellWithIdentifier:@"MessageCellTime"];
if (timeCell == nil) {
timeCell = [[EMChatTimeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MessageCellTime"];
timeCell.backgroundColor = [UIColor clearColor];
timeCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
timeCell.textLabel.text = (NSString *)obj;
return timeCell;
}
else{
MessageModel *model = (MessageModel *)obj;
NSString *cellIdentifier = [EMChatViewCell cellIdentifierForMessageModel:model];
EMChatViewCell *cell = (EMChatViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[EMChatViewCell alloc] initWithMessageModel:model reuseIdentifier:cellIdentifier];
cell.backgroundColor = [UIColor clearColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.messageModel = model;
[cell setDelMessage:^(NSInteger index){
[self deleteMsgWithIndex:index];
}];
cell.tag = indexPath.row;
return cell;
}
}
return nil;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSObject *obj = [_dataSource objectAtIndex:indexPath.row];
if ([obj isKindOfClass:[NSString class]]) {
return 40;
}
else{
return [EMChatViewCell tableView:tableView heightForRowAtIndexPath:indexPath withObject:(MessageModel *)obj];
}
}
#pragma mark - scrollView delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (_slimeView) {
[_slimeView scrollViewDidScroll];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (_slimeView) {
[_slimeView scrollViewDidEndDraging];
}
}
#pragma mark - send message
-(void)sendTextMessage:(NSString *)textMessage
{
EMMessage *tempMessage = [ChatSendHelper sendTextMessageWithString:textMessage
toUsername:_conversation.chatter
messageType:eMessageTypeChat
requireEncryption:NO
ext:[NSDictionary dictionaryWithObject:[AWordUser sharedInstance].userName forKey:@"username"]];
[self addMessage:tempMessage];
}
-(void)sendImageMessage:(UIImage *)image
{
EMMessage *tempMessage = [ChatSendHelper sendImageMessageWithImage:image
toUsername:_conversation.chatter
messageType:eMessageTypeChat
requireEncryption:NO
ext:[NSDictionary dictionaryWithObject:[AWordUser sharedInstance].userName forKey:@"username"]];
[self addMessage:tempMessage];
}
-(void)sendAudioMessage:(EMChatVoice *)voice
{
EMMessage *tempMessage = [ChatSendHelper sendVoice:voice
toUsername:_conversation.chatter
messageType:eMessageTypeChat
requireEncryption:NO ext:[NSDictionary dictionaryWithObject:[AWordUser sharedInstance].userName forKey:@"username"]];
[self addMessage:tempMessage];
}
-(void)sendVideoMessage:(EMChatVideo *)video
{
EMMessage *tempMessage = [ChatSendHelper sendVideo:video
toUsername:_conversation.chatter
messageType:eMessageTypeChat
requireEncryption:NO ext:[NSDictionary dictionaryWithObject:[AWordUser sharedInstance].userName forKey:@"username"]];
[self addMessage:tempMessage];
}
-(void)addMessage:(EMMessage *)message
{
[_messages addObject:message];
__weak ChatMainViewController *weakSelf = self;
dispatch_async(_messageQueue, ^{
NSArray *messages = [weakSelf formatMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[_dataSource addObjectsFromArray:messages];
[weakSelf.tableView reloadData];
[weakSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_dataSource count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
});
});
}
-(NSMutableArray *)formatMessage:(EMMessage *)message
{
NSMutableArray *ret = [[NSMutableArray alloc] init];
NSDate *createDate = [NSDate dateWithTimeIntervalInMilliSecondSince1970:(NSTimeInterval)message.timestamp];
NSTimeInterval tempDate = [createDate timeIntervalSinceDate:_chatTagDate];
if (tempDate > 60 || tempDate < -60 || (_chatTagDate == nil)) {
[ret addObject:[createDate formattedTime]];
_chatTagDate = createDate;
}
MessageModel *model = [MessageModelManager modelWithMessage:message];
if (model.isSender){
model.nickName = [AWordUser sharedInstance].userName;
model.headImageURL = [NSURL URLWithString:[AWordUser sharedInstance].userAvatar];
}else{
model.nickName = _otherUserName;
model.headImageURL = [NSURL URLWithString:_otherUserAvatar];
}
if (model) {
[ret addObject:model];
}
return ret;
}
#pragma mark - LocationViewDelegate
-(void)sendLocationLatitude:(double)latitude longitude:(double)longitude andAddress:(NSString *)address
{
EMMessage *locationMessage = [ChatSendHelper sendLocationLatitude:latitude longitude:longitude address:address toUsername:_conversation.chatter messageType:eMessageTypeChat requireEncryption:NO ext:nil];
[self addMessage:locationMessage];
}
#pragma mark - ICallManagerDelegate
- (void)callSessionStatusChanged:(EMCallSession *)callSession changeReason:(EMCallStatusChangedReason)reason error:(EMError *)error{
if (reason == eCallReason_Null) {
if ([[EMCDDeviceManager sharedInstance] isPlaying]) {
[self stopAudioPlayingWithChangeCategory:NO];
}
}
}
-(void)didSendMessage:(EMMessage *)message error:(EMError *)error
{
[_dataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isKindOfClass:[MessageModel class]])
{
MessageModel *model = (MessageModel*)obj;
if ([model.messageId isEqualToString:message.messageId])
{
model.message.deliveryState = message.deliveryState;
*stop = YES;
}
}
}];
[self.tableView reloadData];
}
- (void)reloadTableViewDataWithMessage:(EMMessage *)message{
__weak ChatMainViewController *weakSelf = self;
dispatch_async(_messageQueue, ^{
if ([_conversation.chatter isEqualToString:message.conversationChatter])
{
for (int i = 0; i < _dataSource.count; i ++) {
id object = [_dataSource objectAtIndex:i];
if ([object isKindOfClass:[MessageModel class]]) {
MessageModel *model = (MessageModel *)object;
if ([message.messageId isEqualToString:model.messageId]) {
MessageModel *cellModel = [MessageModelManager modelWithMessage:message];
cellModel.nickName = cellModel.username;
if (cellModel.isSender){
cellModel.headImageURL = [NSURL URLWithString:[AWordUser sharedInstance].userAvatar];
}else{
cellModel.headImageURL = [NSURL URLWithString:_otherUserAvatar];
}
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView beginUpdates];
[_dataSource replaceObjectAtIndex:i withObject:cellModel];
[weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[weakSelf.tableView endUpdates];
});
break;
}
}
}
}
});
}
-(void)didReceiveMessage:(EMMessage *)message
{
if ([_conversation.chatter isEqualToString:message.conversationChatter]) {
[self addMessage:message];
if ([self shouldAckMessage:message read:NO])
{
[self sendHasReadResponseForMessages:@[message]];
}
if ([self shouldMarkMessageAsRead])
{
[self markMessagesAsRead:@[message]];
}
}
}
- (void)didReceiveMessageId:(NSString *)messageId
chatter:(NSString *)conversationChatter
error:(EMError *)error
{
if (error && [_conversation.chatter isEqualToString:conversationChatter]) {
__weak ChatMainViewController *weakSelf = self;
for (int i = 0; i <_dataSource.count; i ++) {
id object = [_dataSource objectAtIndex:i];
if ([object isKindOfClass:[MessageModel class]]) {
MessageModel *currentModel = [_dataSource objectAtIndex:i];
EMMessage *currMsg = [currentModel message];
if ([messageId isEqualToString:currMsg.messageId]) {
currMsg.deliveryState = eMessageDeliveryState_Failure;
MessageModel *cellModel = [MessageModelManager modelWithMessage:currMsg];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView beginUpdates];
[_dataSource replaceObjectAtIndex:i withObject:cellModel];
[weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[weakSelf.tableView endUpdates];
});
break;
}
}
}
}
}
- (NSArray *)formatMessages:(NSArray *)messagesArray
{
NSMutableArray *formatArray = [[NSMutableArray alloc] init];
if ([messagesArray count] > 0) {
for (EMMessage *message in messagesArray) {
NSDate *createDate = [NSDate dateWithTimeIntervalInMilliSecondSince1970:(NSTimeInterval)message.timestamp];
NSTimeInterval tempDate = [createDate timeIntervalSinceDate:_chatTagDate];
if (tempDate > 60 || tempDate < -60 || (_chatTagDate == nil)) {
[formatArray addObject:[createDate formattedTime]];
_chatTagDate = createDate;
}
MessageModel *model = [MessageModelManager modelWithMessage:message];
if (model.isSender) {
model.nickName = [AWordUser sharedInstance].userName;
}else{
model.nickName = _otherUserName;
}
if (model.isSender){
model.headImageURL = [NSURL URLWithString:[AWordUser sharedInstance].userAvatar];
}else{
model.headImageURL = [NSURL URLWithString:_otherUserAvatar];
}
if (model) {
[formatArray addObject:model];
}
}
}
return formatArray;
}
- (void)downloadMessageAttachments:(MessageModel *)model
{
void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
if (!error)
{
[self reloadTableViewDataWithMessage:model.message];
}
else
{
// [self showHint:NSLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
}
};
if ([model.messageBody messageBodyType] == eMessageBodyType_Image) {
EMImageMessageBody *imageBody = (EMImageMessageBody *)model.messageBody;
if (imageBody.thumbnailDownloadStatus != EMAttachmentDownloadSuccessed)
{
//下载缩略图
[[[EaseMob sharedInstance] chatManager] asyncFetchMessageThumbnail:model.message progress:nil completion:completion onQueue:nil];
}
}
else if ([model.messageBody messageBodyType] == eMessageBodyType_Video)
{
EMVideoMessageBody *videoBody = (EMVideoMessageBody *)model.messageBody;
if (videoBody.thumbnailDownloadStatus != EMAttachmentDownloadSuccessed)
{
//下载缩略图
[[[EaseMob sharedInstance] chatManager] asyncFetchMessageThumbnail:model.message progress:nil completion:completion onQueue:nil];
}
}
else if ([model.messageBody messageBodyType] == eMessageBodyType_Voice)
{
EMVoiceMessageBody *voiceBody = (EMVoiceMessageBody*)model.messageBody;
if (voiceBody.attachmentDownloadStatus != EMAttachmentDownloadSuccessed)
{
//下载语言
[[EaseMob sharedInstance].chatManager asyncFetchMessage:model.message progress:nil];
}
}
}
- (void)didMessageAttachmentsStatusChanged:(EMMessage *)message error:(EMError *)error{
if (!error) {
id<IEMFileMessageBody>fileBody = (id<IEMFileMessageBody>)[message.messageBodies firstObject];
if ([fileBody messageBodyType] == eMessageBodyType_Image) {
EMImageMessageBody *imageBody = (EMImageMessageBody *)fileBody;
if ([imageBody thumbnailDownloadStatus] == EMAttachmentDownloadSuccessed)
{
[self reloadTableViewDataWithMessage:message];
}
}else if([fileBody messageBodyType] == eMessageBodyType_Video){
EMVideoMessageBody *videoBody = (EMVideoMessageBody *)fileBody;
if ([videoBody thumbnailDownloadStatus] == EMAttachmentDownloadSuccessed)
{
[self reloadTableViewDataWithMessage:message];
}
}else if([fileBody messageBodyType] == eMessageBodyType_Voice){
if ([fileBody attachmentDownloadStatus] == EMAttachmentDownloadSuccessed)
{
[self reloadTableViewDataWithMessage:message];
}
}
}else{
}
}
- (void)didFetchingMessageAttachments:(EMMessage *)message progress:(float)progress{
DLog(@"didFetchingMessageAttachment: %f", progress);
}
- (void)loadMoreMessages
{
__weak typeof(self) weakSelf = self;
dispatch_async(_messageQueue, ^{
long long timestamp = [[NSDate date] timeIntervalSince1970] * 1000 + 1;
NSArray *messages = [weakSelf.conversation loadNumbersOfMessages:([_messages count] + KPageCount) before:timestamp];
if ([messages count] > 0) {
_messages = [messages mutableCopy];
NSInteger currentCount = [_dataSource count];
_dataSource = [[weakSelf formatMessages:messages] mutableCopy];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
[weakSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_dataSource count] - currentCount - 1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
});
//从数据库导入时重新下载没有下载成功的附件
for (NSInteger i = 0; i < [_dataSource count]; i++)
{
id obj = _dataSource[i];
if ([obj isKindOfClass:[MessageModel class]])
{
[weakSelf downloadMessageAttachments:obj];
}
}
NSMutableArray *unreadMessages = [NSMutableArray array];
for (NSInteger i = 0; i < [messages count]; i++)
{
EMMessage *message = messages[i];
if ([self shouldAckMessage:message read:NO])
{
[unreadMessages addObject:message];
}
}
if ([unreadMessages count])
{
[self sendHasReadResponseForMessages:unreadMessages];
}
}
});
}
- (BOOL)shouldAckMessage:(EMMessage *)message read:(BOOL)read
{
NSString *account = [[EaseMob sharedInstance].chatManager loginInfo][kSDKUsername];
if (message.messageType != eMessageTypeChat || message.isReadAcked || [account isEqualToString:message.from] || ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) )
{
return NO;
}
id<IEMMessageBody> body = [message.messageBodies firstObject];
if (((body.messageBodyType == eMessageBodyType_Video) ||
(body.messageBodyType == eMessageBodyType_Voice) ||
(body.messageBodyType == eMessageBodyType_Image)) &&
!read)
{
return NO;
}
else
{
return YES;
}
}
- (BOOL)shouldMarkMessageAsRead
{
if (([UIApplication sharedApplication].applicationState == UIApplicationStateBackground))
{
return NO;
}
return YES;
}
- (void)sendHasReadResponseForMessages:(NSArray*)messages
{
dispatch_async(_messageQueue, ^{
for (EMMessage *message in messages)
{
[[EaseMob sharedInstance].chatManager sendReadAckForMessage:message];
}
});
}
- (void)didReceiveHasReadResponse:(EMReceipt *)resp;
{
for (int i=0; i<_dataSource.count; i++) {
MessageModel* message = [_dataSource objectAtIndex:i];
if ([message isKindOfClass:[NSString class]]) {
continue;
}
if ([message.messageId isEqualToString:resp.chatId]) {
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)stopAudioPlayingWithChangeCategory:(BOOL)isChange
{
//停止音频播放及播放动画
[[EMCDDeviceManager sharedInstance] stopPlaying];
[[EMCDDeviceManager sharedInstance] disableProximitySensor];
[EMCDDeviceManager sharedInstance].delegate = nil;
MessageModel *playingModel = [self.messageReadManager stopMessageAudioModel];
NSIndexPath *indexPath = nil;
if (playingModel) {
indexPath = [NSIndexPath indexPathForRow:[_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];
});
}
}
#pragma mark - EMCDDeviceManagerDelegate
- (void)proximitySensorChanged:(BOOL)isCloseToUser{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
if (isCloseToUser)
{
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
} else {
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
if (!_isPlayingAudio) {
[[EMCDDeviceManager sharedInstance] disableProximitySensor];
}
}
[audioSession setActive:YES error:nil];
}
- (void)markMessagesAsRead:(NSArray*)messages
{
EMConversation *conversation = _conversation;
dispatch_async(_messageQueue, ^{
for (EMMessage *message in messages)
{
[conversation markMessageWithId:message.messageId asRead:YES];
}
});
}
#pragma mark - UIResponder actions
- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{
MessageModel *model = [userInfo objectForKey:KMESSAGEKEY];
if ([eventName isEqualToString:kRouterEventTextURLTapEventName]) {
[self chatTextCellUrlPressed:[userInfo objectForKey:@"url"]];
}
else if ([eventName isEqualToString:kRouterEventAudioBubbleTapEventName]) {
[self chatAudioCellBubblePressed:model];
}
else if ([eventName isEqualToString:kRouterEventImageBubbleTapEventName]){
[self chatImageCellBubblePressed:model];
}
else if ([eventName isEqualToString:kRouterEventLocationBubbleTapEventName]){
[self chatLocationCellBubblePressed:model];
}
else if([eventName isEqualToString:kResendButtonTapEventName]){
EMChatViewCell *resendCell = [userInfo objectForKey:kShouldResendCell];
MessageModel *messageModel = resendCell.messageModel;
if ((messageModel.status != eMessageDeliveryState_Failure) && (messageModel.status != eMessageDeliveryState_Pending))
{
return;
}
id <IChatManager> chatManager = [[EaseMob sharedInstance] chatManager];
[chatManager asyncResendMessage:messageModel.message progress:nil];
NSIndexPath *indexPath = [self.tableView indexPathForCell:resendCell];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}else if([eventName isEqualToString:kRouterEventChatCellVideoTapEventName]){
[self chatVideoCellPressed:model];
}else if([eventName isEqualToString:kRouterEventChatHeadImageTapEventName]){
if ([_otherUserId isEqualToString:@"admin"]) {
return;
}
PersonalInfoViewController* personalInfoViewController = [[PersonalInfoViewController alloc] init];
personalInfoViewController.hidesBottomBarWhenPushed = YES;
personalInfoViewController.otherUserId = _otherUserId;
[[AppDelegate visibleViewController].navigationController pushViewController:personalInfoViewController animated:YES];
}
}
//链接被点击
- (void)chatTextCellUrlPressed:(NSURL *)url
{
if (url) {
[[UIApplication sharedApplication] openURL:url];
}
}
// 语音的bubble被点击
-(void)chatAudioCellBubblePressed:(MessageModel *)model
{
id <IEMFileMessageBody> body = [model.message.messageBodies firstObject];
EMAttachmentDownloadStatus downloadStatus = [body attachmentDownloadStatus];
if (downloadStatus == EMAttachmentDownloading) {
// [self showHint:NSLocalizedString(@"message.downloadingAudio", @"downloading voice, click later")];
[MBProgressHUD checkHudWithView:self.view label:NSLocalizedString(@"message.downloadingAudio", @"downloading voice, click later") hidesAfter:1.0];
return;
}
else if (downloadStatus == EMAttachmentDownloadFailure)
{
[MBProgressHUD checkHudWithView:self.view label:NSLocalizedString(@"message.downloadingAudio", @"downloading voice, click later") hidesAfter:1.0];
[[EaseMob sharedInstance].chatManager asyncFetchMessage:model.message progress:nil];
return;
}
// 播放音频
if (model.type == eMessageBodyType_Voice) {
//发送已读回执
if ([self shouldAckMessage:model.message read:YES])
{
[self sendHasReadResponseForMessages:@[model.message]];
}
__weak ChatMainViewController *weakSelf = self;
BOOL isPrepare = [self.messageReadManager prepareMessageAudioModel:model updateViewCompletion:^(MessageModel *prevAudioModel, MessageModel *currentAudioModel) {
if (prevAudioModel || currentAudioModel) {
[weakSelf.tableView reloadData];
}
}];
if (isPrepare) {
_isPlayingAudio = YES;
__weak ChatMainViewController *weakSelf = self;
[[EMCDDeviceManager sharedInstance] enableProximitySensor];
[[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:model.chatVoice.localPath completion:^(NSError *error) {
[weakSelf.messageReadManager stopMessageAudioModel];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
weakSelf.isPlayingAudio = NO;
[[EMCDDeviceManager sharedInstance] disableProximitySensor];
});
}];
}
else{
_isPlayingAudio = NO;
}
}
}
// 位置的bubble被点击
-(void)chatLocationCellBubblePressed:(MessageModel *)model
{
// _isScrollToBottom = NO;
LocationViewController *locationController = [[LocationViewController alloc] initWithLocation:CLLocationCoordinate2DMake(model.latitude, model.longitude)];
[self.navigationController pushViewController:locationController animated:YES];
}
- (void)chatVideoCellPressed:(MessageModel *)model{
EMVideoMessageBody *videoBody = (EMVideoMessageBody*)model.messageBody;
if (videoBody.attachmentDownloadStatus == EMAttachmentDownloadSuccessed)
{
NSString *localPath = model.message == nil ? model.localPath : [[model.message.messageBodies firstObject] localPath];
if (localPath && localPath.length > 0)
{
//发送已读回执
if ([self shouldAckMessage:model.message read:YES])
{
[self sendHasReadResponseForMessages:@[model.message]];
}
[self playVideoWithVideoPath:localPath];
return;
}
}
__weak ChatMainViewController *weakSelf = self;
id <IChatManager> chatManager = [[EaseMob sharedInstance] chatManager];
[MBProgressHUD hudWithView:self.view label:NSLocalizedString(@"message.downloadingVideo", @"downloading video...")];
[chatManager asyncFetchMessage:model.message progress:nil completion:^(EMMessage *aMessage, EMError *error) {
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
if (!error) {
//发送已读回执
if ([weakSelf shouldAckMessage:model.message read:YES])
{
[weakSelf sendHasReadResponseForMessages:@[model.message]];
}
NSString *localPath = aMessage == nil ? model.localPath : [[aMessage.messageBodies firstObject] localPath];
if (localPath && localPath.length > 0) {
[weakSelf playVideoWithVideoPath:localPath];
}
}else{
[MBProgressHUD checkHudWithView:self.view label:NSLocalizedString(@"message.videoFail", @"video for failure!") hidesAfter:1.0];
}
} onQueue:nil];
}
- (void)playVideoWithVideoPath:(NSString *)videoPath
{
// _isScrollToBottom = NO;
NSURL *videoURL = [NSURL fileURLWithPath:videoPath];
MPMoviePlayerViewController *moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
[moviePlayerController.moviePlayer prepareToPlay];
moviePlayerController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[self presentMoviePlayerViewControllerAnimated:moviePlayerController];
}
// 图片的bubble被点击
-(void)chatImageCellBubblePressed:(MessageModel *)model
{
__weak ChatMainViewController *weakSelf = self;
id <IChatManager> chatManager = [[EaseMob sharedInstance] chatManager];
if ([model.messageBody messageBodyType] == eMessageBodyType_Image) {
EMImageMessageBody *imageBody = (EMImageMessageBody *)model.messageBody;
if (imageBody.thumbnailDownloadStatus == EMAttachmentDownloadSuccessed) {
if (imageBody.attachmentDownloadStatus == EMAttachmentDownloadSuccessed)
{
//发送已读回执
if ([self shouldAckMessage:model.message read:YES])
{
[self sendHasReadResponseForMessages:@[model.message]];
}
NSString *localPath = model.message == nil ? model.localPath : [[model.message.messageBodies firstObject] localPath];
if (localPath && localPath.length > 0) {
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
// self.isScrollToBottom = NO;
if (image)
{
[self.messageReadManager showBrowserWithImages:@[image]];
}
else
{
NSLog(@"Read %@ failed!", localPath);
}
return ;
}
}
[MBProgressHUD hudWithView:self.view label:NSLocalizedString(@"message.downloadingImage", @"downloading a image...")];
[chatManager asyncFetchMessage:model.message progress:nil completion:^(EMMessage *aMessage, EMError *error) {
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
if (!error) {
//发送已读回执
if ([weakSelf shouldAckMessage:model.message read:YES])
{
[weakSelf sendHasReadResponseForMessages:@[model.message]];
}
NSString *localPath = aMessage == nil ? model.localPath : [[aMessage.messageBodies firstObject] localPath];
if (localPath && localPath.length > 0) {
UIImage *image = [UIImage imageWithContentsOfFile:localPath];
// weakSelf.isScrollToBottom = NO;
if (image)
{
[weakSelf.messageReadManager showBrowserWithImages:@[image]];
}
else
{
NSLog(@"Read %@ failed!", localPath);
}
return ;
}
}
[MBProgressHUD errorHudWithView:self.view label:NSLocalizedString(@"message.imageFail", @"image for failure!") hidesAfter:1.0];
} onQueue:nil];
}else{
//获取缩略图
[chatManager asyncFetchMessageThumbnail:model.message progress:nil completion:^(EMMessage *aMessage, EMError *error) {
if (!error) {
[weakSelf reloadTableViewDataWithMessage:model.message];
}else{
// [weakSelf showHint:NSLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
[MBProgressHUD errorHudWithView:self.view label:NSLocalizedString(@"message.thumImageFail", @"thumbnail for failure!") hidesAfter:1.0];
}
} onQueue:nil];
}
}else if ([model.messageBody messageBodyType] == eMessageBodyType_Video) {
//获取缩略图
EMVideoMessageBody *videoBody = (EMVideoMessageBody *)model.messageBody;
if (videoBody.thumbnailDownloadStatus != EMAttachmentDownloadSuccessed) {
[chatManager asyncFetchMessageThumbnail:model.message progress:nil completion:^(EMMessage *aMessage, EMError *error) {
if (!error) {
[weakSelf reloadTableViewDataWithMessage:model.message];
}else{
[MBProgressHUD errorHudWithView:self.view label:NSLocalizedString(@"message.thumImageFail", @"thumbnail for failure!") hidesAfter:1.0];
}
} onQueue:nil];
}
}
}
- (void)deleteMsgWithIndex:(NSInteger)index;
{
if (index > 0) {
MessageModel *model = [_dataSource objectAtIndex:index];
NSMutableIndexSet *indexs = [NSMutableIndexSet indexSetWithIndex:index];
[_conversation removeMessage:model.message];
[_messages removeObject:model.message];
NSMutableArray *indexPaths = [NSMutableArray arrayWithObjects:[NSIndexPath indexPathForRow:index inSection:0], nil];;
if (index - 1 >= 0) {
id nextMessage = nil;
id prevMessage = [_dataSource objectAtIndex:(index- 1)];
if (index + 1 < [_dataSource count]) {
nextMessage = [_dataSource objectAtIndex:(index + 1)];
}
if ((!nextMessage || [nextMessage isKindOfClass:[NSString class]]) && [prevMessage isKindOfClass:[NSString class]]) {
[indexs addIndex:index - 1];
[indexPaths addObject:[NSIndexPath indexPathForRow:(index - 1) inSection:0]];
}
}
[_dataSource removeObjectsAtIndexes:indexs];
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)dealloc
{
_slimeView.delegate = nil;
_slimeView = nil;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end