New file |
| | |
| | | /************************************************************ |
| | | * * 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 |