EaseMessageViewController.m 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255
  1. /************************************************************
  2. * * Hyphenate CONFIDENTIAL
  3. * __________________
  4. * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
  5. *
  6. * NOTICE: All information contained herein is, and remains
  7. * the property of Hyphenate Inc.
  8. * Dissemination of this information or reproduction of this material
  9. * is strictly forbidden unless prior written permission is obtained
  10. * from Hyphenate Inc.
  11. */
  12. #import "EaseMessageViewController.h"
  13. #import <Foundation/Foundation.h>
  14. #import <Photos/Photos.h>
  15. #import <AssetsLibrary/AssetsLibrary.h>
  16. #import "NSDate+Category.h"
  17. #import "EaseUsersListViewController.h"
  18. #import "EaseMessageReadManager.h"
  19. #import "EaseEmotionManager.h"
  20. #import "EaseEmoji.h"
  21. #import "EaseEmotionEscape.h"
  22. #import "EaseCustomMessageCell.h"
  23. #import "UIImage+EMGIF.h"
  24. #import "EaseLocalDefine.h"
  25. #import "EaseSDKHelper.h"
  26. #define KHintAdjustY 50
  27. #define IOS_VERSION [[UIDevice currentDevice] systemVersion]>=9.0
  28. typedef enum : NSUInteger {
  29. EMRequestRecord,
  30. EMCanRecord,
  31. EMCanNotRecord,
  32. } EMRecordResponse;
  33. @implementation EaseAtTarget
  34. - (instancetype)initWithUserId:(NSString*)userId andNickname:(NSString*)nickname
  35. {
  36. if (self = [super init]) {
  37. _userId = [userId copy];
  38. _nickname = [nickname copy];
  39. }
  40. return self;
  41. }
  42. @end
  43. @interface EaseMessageViewController ()<EaseMessageCellDelegate, EMChatManagerDelegate>
  44. {
  45. UIMenuItem *_copyMenuItem;
  46. UIMenuItem *_deleteMenuItem;
  47. UILongPressGestureRecognizer *_lpgr;
  48. NSMutableArray *_atTargets;
  49. dispatch_queue_t _messageQueue;
  50. BOOL _isRecording;
  51. }
  52. @property (strong, nonatomic) id<IMessageModel> playingVoiceModel;
  53. @property (nonatomic) BOOL isKicked;
  54. @property (nonatomic) BOOL isPlayingAudio;
  55. @property (nonatomic, strong) NSMutableArray *atTargets;
  56. @end
  57. @implementation EaseMessageViewController
  58. @synthesize conversation = _conversation;
  59. @synthesize deleteConversationIfNull = _deleteConversationIfNull;
  60. @synthesize messageCountOfPage = _messageCountOfPage;
  61. @synthesize timeCellHeight = _timeCellHeight;
  62. @synthesize messageTimeIntervalTag = _messageTimeIntervalTag;
  63. - (instancetype)initWithConversationChatter:(NSString *)conversationChatter
  64. conversationType:(EMConversationType)conversationType
  65. {
  66. if ([conversationChatter length] == 0) {
  67. return nil;
  68. }
  69. self = [super initWithStyle:UITableViewStylePlain];
  70. if (self) {
  71. _conversation = [[EMClient sharedClient].chatManager getConversation:conversationChatter type:conversationType createIfNotExist:YES];
  72. _messageCountOfPage = 10;
  73. _timeCellHeight = 30;
  74. _deleteConversationIfNull = YES;
  75. _scrollToBottomWhenAppear = YES;
  76. _messsagesSource = [NSMutableArray array];
  77. self.navigationController.navigationBar.translucent = NO;
  78. [_conversation markAllMessagesAsRead:nil];
  79. }
  80. return self;
  81. }
  82. - (void)viewDidLoad {
  83. [super viewDidLoad];
  84. [[EMClient sharedClient] addDelegate:self delegateQueue:dispatch_get_main_queue()];
  85. self.view.backgroundColor = [UIColor colorWithRed:248 / 255.0 green:248 / 255.0 blue:248 / 255.0 alpha:1.0];
  86. self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  87. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hideImagePicker) name:@"hideImagePicker" object:nil];
  88. //Initialization
  89. CGFloat chatbarHeight = [EaseChatToolbar defaultHeight];
  90. EMChatToolbarType barType = self.conversation.type == EMConversationTypeChat ? EMChatToolbarTypeChat : EMChatToolbarTypeGroup;
  91. self.chatToolbar = [[EaseChatToolbar alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - chatbarHeight, self.view.frame.size.width, chatbarHeight) type:barType];
  92. self.chatToolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  93. //Initializa the gesture recognizer
  94. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(keyBoardHidden:)];
  95. [self.view addGestureRecognizer:tap];
  96. _lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
  97. _lpgr.minimumPressDuration = 0.5;
  98. [self.tableView addGestureRecognizer:_lpgr];
  99. _messageQueue = dispatch_queue_create("hyphenate.com", NULL);
  100. //Register the delegate
  101. [EMCDDeviceManager sharedInstance].delegate = self;
  102. [[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
  103. [[EMClient sharedClient].roomManager addDelegate:self delegateQueue:nil];
  104. if (self.conversation.type == EMConversationTypeChatRoom)
  105. {
  106. [self joinChatroom:self.conversation.conversationId];
  107. }
  108. [[NSNotificationCenter defaultCenter] addObserver:self
  109. selector:@selector(didBecomeActive)
  110. name:UIApplicationDidBecomeActiveNotification
  111. object:nil];
  112. [[EaseBaseMessageCell appearance] setSendBubbleBackgroundImage:[[UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_bg"] stretchableImageWithLeftCapWidth:5 topCapHeight:35]];
  113. [[EaseBaseMessageCell appearance] setRecvBubbleBackgroundImage:[[UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_bg"] stretchableImageWithLeftCapWidth:35 topCapHeight:35]];
  114. [[EaseBaseMessageCell appearance] setSendMessageVoiceAnimationImages:@[[UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_full"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_000"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_001"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_002"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_003"]]];
  115. [[EaseBaseMessageCell appearance] setRecvMessageVoiceAnimationImages:@[[UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing_full"],[UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing000"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing001"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing002"], [UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing003"]]];
  116. [[EaseBaseMessageCell appearance] setAvatarSize:40.f];
  117. [[EaseBaseMessageCell appearance] setAvatarCornerRadius:20.f];
  118. [[EaseChatBarMoreView appearance] setMoreViewBackgroundColor:[UIColor colorWithRed:240 / 255.0 green:242 / 255.0 blue:247 / 255.0 alpha:1.0]];
  119. [self tableViewDidTriggerHeaderRefresh];
  120. [self setupEmotion];
  121. }
  122. /*!
  123. @method
  124. @brief 设置表情
  125. @discussion 加载默认表情,如果子类实现了dataSource的自定义表情回调,同时会加载自定义表情
  126. @result
  127. */
  128. - (void)setupEmotion
  129. {
  130. if ([self.dataSource respondsToSelector:@selector(emotionFormessageViewController:)]) {
  131. NSArray* emotionManagers = [self.dataSource emotionFormessageViewController:self];
  132. [self.faceView setEmotionManagers:emotionManagers];
  133. } else {
  134. NSMutableArray *emotions = [NSMutableArray array];
  135. for (NSString *name in [EaseEmoji allEmoji]) {
  136. EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name emotionThumbnail:name emotionOriginal:name emotionOriginalURL:@"" emotionType:EMEmotionDefault];
  137. [emotions addObject:emotion];
  138. }
  139. EaseEmotion *emotion = [emotions objectAtIndex:0];
  140. EaseEmotionManager *manager= [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:emotions tagImage:[UIImage imageNamed:emotion.emotionId]];
  141. [self.faceView setEmotionManagers:@[manager]];
  142. }
  143. }
  144. - (void)didReceiveMemoryWarning {
  145. [super didReceiveMemoryWarning];
  146. // Dispose of any resources that can be recreated.
  147. }
  148. - (void)dealloc
  149. {
  150. [[NSNotificationCenter defaultCenter] removeObserver:self];
  151. [[EMCDDeviceManager sharedInstance] stopPlaying];
  152. [EMCDDeviceManager sharedInstance].delegate = nil;
  153. if (_imagePicker){
  154. [_imagePicker dismissViewControllerAnimated:NO completion:nil];
  155. _imagePicker = nil;
  156. }
  157. }
  158. - (void)viewWillAppear:(BOOL)animated
  159. {
  160. [super viewWillAppear:animated];
  161. self.isViewDidAppear = YES;
  162. [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
  163. if (self.scrollToBottomWhenAppear) {
  164. [self _scrollViewToBottom:NO];
  165. }
  166. self.scrollToBottomWhenAppear = YES;
  167. }
  168. - (void)viewWillDisappear:(BOOL)animated
  169. {
  170. [super viewWillDisappear:animated];
  171. self.isViewDidAppear = NO;
  172. [[EMCDDeviceManager sharedInstance] disableProximitySensor];
  173. }
  174. #pragma mark - chatroom
  175. - (void)saveChatroom:(EMChatroom *)chatroom
  176. {
  177. NSString *chatroomName = chatroom.subject ? chatroom.subject : @"";
  178. NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
  179. NSString *key = [NSString stringWithFormat:@"OnceJoinedChatrooms_%@", [[EMClient sharedClient] currentUsername]];
  180. NSMutableDictionary *chatRooms = [NSMutableDictionary dictionaryWithDictionary:[ud objectForKey:key]];
  181. if (![chatRooms objectForKey:chatroom.chatroomId])
  182. {
  183. [chatRooms setObject:chatroomName forKey:chatroom.chatroomId];
  184. [ud setObject:chatRooms forKey:key];
  185. [ud synchronize];
  186. }
  187. }
  188. /*!
  189. @method
  190. @brief 加入聊天室
  191. @discussion
  192. @result
  193. */
  194. - (void)joinChatroom:(NSString *)chatroomId
  195. {
  196. __weak typeof(self) weakSelf = self;
  197. [self showHudInView:self.view hint:NSEaseLocalizedString(@"chatroom.joining",@"Joining the chatroom")];
  198. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  199. EMError *error = nil;
  200. EMChatroom *chatroom = [[EMClient sharedClient].roomManager joinChatroom:chatroomId error:&error];
  201. dispatch_async(dispatch_get_main_queue(), ^{
  202. if (weakSelf) {
  203. EaseMessageViewController *strongSelf = weakSelf;
  204. [strongSelf hideHud];
  205. if (error != nil) {
  206. [strongSelf showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.joinFailed",@"join chatroom \'%@\' failed"), chatroomId]];
  207. } else {
  208. strongSelf.isJoinedChatroom = YES;
  209. [strongSelf saveChatroom:chatroom];
  210. }
  211. } else {
  212. if (!error || (error.code == EMErrorChatroomAlreadyJoined)) {
  213. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  214. EMError *leaveError;
  215. [[EMClient sharedClient].roomManager leaveChatroom:chatroomId error:&leaveError];
  216. [[EMClient sharedClient].chatManager deleteConversation:chatroomId isDeleteMessages:YES completion:nil];
  217. });
  218. }
  219. }
  220. });
  221. });
  222. }
  223. #pragma mark - EMChatManagerChatroomDelegate
  224. - (void)didReceiveUserJoinedChatroom:(EMChatroom *)aChatroom
  225. username:(NSString *)aUsername
  226. {
  227. CGRect frame = self.chatToolbar.frame;
  228. [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.join", @"\'%@\'join chatroom\'%@\'"), aUsername, aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
  229. }
  230. - (void)didReceiveUserLeavedChatroom:(EMChatroom *)aChatroom
  231. username:(NSString *)aUsername
  232. {
  233. CGRect frame = self.chatToolbar.frame;
  234. [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.leave.hint", @"\'%@\'leave chatroom\'%@\'"), aUsername, aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
  235. }
  236. - (void)didReceiveKickedFromChatroom:(EMChatroom *)aChatroom
  237. reason:(EMChatroomBeKickedReason)aReason
  238. {
  239. if ([_conversation.conversationId isEqualToString:aChatroom.chatroomId])
  240. {
  241. _isKicked = YES;
  242. CGRect frame = self.chatToolbar.frame;
  243. [self showHint:[NSString stringWithFormat:NSEaseLocalizedString(@"chatroom.remove", @"be removed from chatroom\'%@\'"), aChatroom.chatroomId] yOffset:-frame.size.height + KHintAdjustY];
  244. [self.navigationController popToViewController:self animated:NO];
  245. [self.navigationController popViewControllerAnimated:YES];
  246. }
  247. }
  248. #pragma mark - getter
  249. - (UIImagePickerController *)imagePicker
  250. {
  251. if (_imagePicker == nil) {
  252. _imagePicker = [[UIImagePickerController alloc] init];
  253. _imagePicker.modalPresentationStyle= UIModalPresentationOverFullScreen;
  254. _imagePicker.delegate = self;
  255. }
  256. return _imagePicker;
  257. }
  258. - (NSMutableArray*)atTargets
  259. {
  260. if (!_atTargets) {
  261. _atTargets = [NSMutableArray array];
  262. }
  263. return _atTargets;
  264. }
  265. #pragma mark - setter
  266. - (void)setIsViewDidAppear:(BOOL)isViewDidAppear
  267. {
  268. _isViewDidAppear =isViewDidAppear;
  269. if (_isViewDidAppear)
  270. {
  271. NSMutableArray *unreadMessages = [NSMutableArray array];
  272. for (EMMessage *message in self.messsagesSource)
  273. {
  274. if ([self shouldSendHasReadAckForMessage:message read:NO])
  275. {
  276. [unreadMessages addObject:message];
  277. }
  278. }
  279. if ([unreadMessages count])
  280. {
  281. [self _sendHasReadResponseForMessages:unreadMessages isRead:YES];
  282. }
  283. [_conversation markAllMessagesAsRead:nil];
  284. }
  285. }
  286. - (void)setChatToolbar:(EaseChatToolbar *)chatToolbar
  287. {
  288. [_chatToolbar removeFromSuperview];
  289. _chatToolbar = chatToolbar;
  290. if (_chatToolbar) {
  291. [self.view addSubview:_chatToolbar];
  292. }
  293. CGRect tableFrame = self.tableView.frame;
  294. tableFrame.size.height = self.view.frame.size.height - _chatToolbar.frame.size.height;
  295. self.tableView.frame = tableFrame;
  296. if ([chatToolbar isKindOfClass:[EaseChatToolbar class]]) {
  297. [(EaseChatToolbar *)self.chatToolbar setDelegate:self];
  298. self.chatBarMoreView = (EaseChatBarMoreView*)[(EaseChatToolbar *)self.chatToolbar moreView];
  299. self.faceView = (EaseFaceView*)[(EaseChatToolbar *)self.chatToolbar faceView];
  300. self.recordView = (EaseRecordView*)[(EaseChatToolbar *)self.chatToolbar recordView];
  301. }
  302. }
  303. - (void)setDataSource:(id<EaseMessageViewControllerDataSource>)dataSource
  304. {
  305. _dataSource = dataSource;
  306. [self setupEmotion];
  307. }
  308. - (void)setDelegate:(id<EaseMessageViewControllerDelegate>)delegate
  309. {
  310. _delegate = delegate;
  311. }
  312. #pragma mark - private helper
  313. /*!
  314. @method
  315. @brief tableView滑动到底部
  316. @discussion
  317. @result
  318. */
  319. - (void)_scrollViewToBottom:(BOOL)animated
  320. {
  321. if (self.tableView.contentSize.height > self.tableView.frame.size.height)
  322. {
  323. CGPoint offset = CGPointMake(0, self.tableView.contentSize.height - self.tableView.frame.size.height);
  324. [self.tableView setContentOffset:offset animated:animated];
  325. }
  326. }
  327. /*!
  328. @method
  329. @brief 当前设备是否可以录音
  330. @discussion
  331. @param aComplation 判断结果
  332. @result
  333. */
  334. - (void)_canRecordComplation:(void(^)(EMRecordResponse))aComplation
  335. {
  336. AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  337. if (videoAuthStatus == AVAuthorizationStatusNotDetermined) {
  338. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  339. if (aComplation) {
  340. aComplation(EMRequestRecord);
  341. }
  342. }];
  343. }
  344. else if(videoAuthStatus == AVAuthorizationStatusRestricted || videoAuthStatus == AVAuthorizationStatusDenied) {
  345. aComplation(EMCanNotRecord);
  346. }
  347. else{
  348. aComplation(EMCanRecord);
  349. }
  350. }
  351. - (void)showMenuViewController:(UIView *)showInView
  352. andIndexPath:(NSIndexPath *)indexPath
  353. messageType:(EMMessageBodyType)messageType
  354. {
  355. if (_menuController == nil) {
  356. _menuController = [UIMenuController sharedMenuController];
  357. }
  358. if (_deleteMenuItem == nil) {
  359. _deleteMenuItem = [[UIMenuItem alloc] initWithTitle:NSEaseLocalizedString(@"delete", @"Delete") action:@selector(deleteMenuAction:)];
  360. }
  361. if (_copyMenuItem == nil) {
  362. _copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSEaseLocalizedString(@"copy", @"Copy") action:@selector(copyMenuAction:)];
  363. }
  364. if (messageType == EMMessageBodyTypeText) {
  365. [_menuController setMenuItems:@[_copyMenuItem, _deleteMenuItem]];
  366. } else {
  367. [_menuController setMenuItems:@[_deleteMenuItem]];
  368. }
  369. [_menuController setTargetRect:showInView.frame inView:showInView.superview];
  370. [_menuController setMenuVisible:YES animated:YES];
  371. }
  372. - (void)_stopAudioPlayingWithChangeCategory:(BOOL)isChange
  373. {
  374. //停止音频播放及播放动画
  375. [[EMCDDeviceManager sharedInstance] stopPlaying];
  376. [[EMCDDeviceManager sharedInstance] disableProximitySensor];
  377. [EMCDDeviceManager sharedInstance].delegate = nil;
  378. // MessageModel *playingModel = [self.EaseMessageReadManager stopMessageAudioModel];
  379. // NSIndexPath *indexPath = nil;
  380. // if (playingModel) {
  381. // indexPath = [NSIndexPath indexPathForRow:[self.dataSource indexOfObject:playingModel] inSection:0];
  382. // }
  383. //
  384. // if (indexPath) {
  385. // dispatch_async(dispatch_get_main_queue(), ^{
  386. // [self.tableView beginUpdates];
  387. // [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
  388. // [self.tableView endUpdates];
  389. // });
  390. // }
  391. }
  392. /*!
  393. @method
  394. @brief mov格式视频转换为MP4格式
  395. @discussion
  396. @param movUrl mov视频路径
  397. @result MP4格式视频路径
  398. */
  399. - (NSURL *)_convert2Mp4:(NSURL *)movUrl
  400. {
  401. NSURL *mp4Url = nil;
  402. AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:movUrl options:nil];
  403. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
  404. if ([compatiblePresets containsObject:AVAssetExportPresetHighestQuality]) {
  405. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset
  406. presetName:AVAssetExportPresetHighestQuality];
  407. NSString *mp4Path = [NSString stringWithFormat:@"%@/%d%d.mp4", [EMCDDeviceManager dataPath], (int)[[NSDate date] timeIntervalSince1970], arc4random() % 100000];
  408. mp4Url = [NSURL fileURLWithPath:mp4Path];
  409. exportSession.outputURL = mp4Url;
  410. exportSession.shouldOptimizeForNetworkUse = YES;
  411. exportSession.outputFileType = AVFileTypeMPEG4;
  412. dispatch_semaphore_t wait = dispatch_semaphore_create(0l);
  413. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  414. switch ([exportSession status]) {
  415. case AVAssetExportSessionStatusFailed: {
  416. NSLog(@"failed, error:%@.", exportSession.error);
  417. } break;
  418. case AVAssetExportSessionStatusCancelled: {
  419. NSLog(@"cancelled.");
  420. } break;
  421. case AVAssetExportSessionStatusCompleted: {
  422. NSLog(@"completed.");
  423. } break;
  424. default: {
  425. NSLog(@"others.");
  426. } break;
  427. }
  428. dispatch_semaphore_signal(wait);
  429. }];
  430. long timeout = dispatch_semaphore_wait(wait, DISPATCH_TIME_FOREVER);
  431. if (timeout) {
  432. NSLog(@"timeout.");
  433. }
  434. if (wait) {
  435. //dispatch_release(wait);
  436. wait = nil;
  437. }
  438. }
  439. return mp4Url;
  440. }
  441. /*!
  442. @method
  443. @brief 通过当前会话类型,返回消息聊天类型
  444. @discussion
  445. @result
  446. */
  447. - (EMChatType)_messageTypeFromConversationType
  448. {
  449. EMChatType type = EMChatTypeChat;
  450. switch (self.conversation.type) {
  451. case EMConversationTypeChat:
  452. type = EMChatTypeChat;
  453. break;
  454. case EMConversationTypeGroupChat:
  455. type = EMChatTypeGroupChat;
  456. break;
  457. case EMConversationTypeChatRoom:
  458. type = EMChatTypeChatRoom;
  459. break;
  460. default:
  461. break;
  462. }
  463. return type;
  464. }
  465. /*!
  466. @method
  467. @brief 下载消息附件
  468. @discussion
  469. @param message 待下载附件的消息
  470. @result
  471. */
  472. - (void)_downloadMessageAttachments:(EMMessage *)message
  473. {
  474. __weak typeof(self) weakSelf = self;
  475. void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
  476. if (!error)
  477. {
  478. [weakSelf _reloadTableViewDataWithMessage:message];
  479. }
  480. else
  481. {
  482. [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
  483. }
  484. };
  485. EMMessageBody *messageBody = message.body;
  486. if ([messageBody type] == EMMessageBodyTypeImage) {
  487. EMImageMessageBody *imageBody = (EMImageMessageBody *)messageBody;
  488. if (imageBody.thumbnailDownloadStatus > EMDownloadStatusSuccessed)
  489. {
  490. //download the message thumbnail
  491. [[[EMClient sharedClient] chatManager] downloadMessageThumbnail:message progress:nil completion:completion];
  492. }
  493. }
  494. else if ([messageBody type] == EMMessageBodyTypeVideo)
  495. {
  496. EMVideoMessageBody *videoBody = (EMVideoMessageBody *)messageBody;
  497. if (videoBody.thumbnailDownloadStatus > EMDownloadStatusSuccessed)
  498. {
  499. //download the message thumbnail
  500. [[[EMClient sharedClient] chatManager] downloadMessageThumbnail:message progress:nil completion:completion];
  501. }
  502. }
  503. else if ([messageBody type] == EMMessageBodyTypeVoice)
  504. {
  505. EMVoiceMessageBody *voiceBody = (EMVoiceMessageBody*)messageBody;
  506. if (voiceBody.downloadStatus > EMDownloadStatusSuccessed)
  507. {
  508. //download the message attachment
  509. [[EMClient sharedClient].chatManager downloadMessageAttachment:message progress:nil completion:^(EMMessage *message, EMError *error) {
  510. if (!error) {
  511. [weakSelf _reloadTableViewDataWithMessage:message];
  512. }
  513. else {
  514. [weakSelf showHint:NSEaseLocalizedString(@"message.voiceFail", @"voice for failure!")];
  515. }
  516. }];
  517. }
  518. }
  519. }
  520. /*!
  521. @method
  522. @brief 传入消息是否需要发动已读回执
  523. @discussion
  524. @param message 待判断的消息
  525. @param read 消息是否已读
  526. @result
  527. */
  528. - (BOOL)shouldSendHasReadAckForMessage:(EMMessage *)message
  529. read:(BOOL)read
  530. {
  531. NSString *account = [[EMClient sharedClient] currentUsername];
  532. if (message.chatType != EMChatTypeChat || message.isReadAcked || message.direction == EMMessageDirectionSend || ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) || !self.isViewDidAppear)
  533. {
  534. return NO;
  535. }
  536. EMMessageBody *body = message.body;
  537. if (((body.type == EMMessageBodyTypeVideo) ||
  538. (body.type == EMMessageBodyTypeVoice) ||
  539. (body.type == EMMessageBodyTypeImage)) &&
  540. !read)
  541. {
  542. return NO;
  543. }
  544. else
  545. {
  546. return YES;
  547. }
  548. }
  549. /*!
  550. @method
  551. @brief 为传入的消息发送已读回执
  552. @discussion
  553. @param messages 待发送已读回执的消息数组
  554. @param isRead 是否已读
  555. @result
  556. */
  557. - (void)_sendHasReadResponseForMessages:(NSArray*)messages
  558. isRead:(BOOL)isRead
  559. {
  560. NSMutableArray *unreadMessages = [NSMutableArray array];
  561. for (NSInteger i = 0; i < [messages count]; i++)
  562. {
  563. EMMessage *message = messages[i];
  564. BOOL isSend = YES;
  565. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:shouldSendHasReadAckForMessage:read:)]) {
  566. isSend = [_dataSource messageViewController:self
  567. shouldSendHasReadAckForMessage:message read:isRead];
  568. }
  569. else{
  570. isSend = [self shouldSendHasReadAckForMessage:message
  571. read:isRead];
  572. }
  573. if (isSend)
  574. {
  575. [unreadMessages addObject:message];
  576. }
  577. }
  578. if ([unreadMessages count])
  579. {
  580. for (EMMessage *message in unreadMessages)
  581. {
  582. [[EMClient sharedClient].chatManager sendMessageReadAck:message completion:nil];
  583. }
  584. }
  585. }
  586. - (BOOL)_shouldMarkMessageAsRead
  587. {
  588. BOOL isMark = YES;
  589. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewControllerShouldMarkMessagesAsRead:)]) {
  590. isMark = [_dataSource messageViewControllerShouldMarkMessagesAsRead:self];
  591. }
  592. else{
  593. if (([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) || !self.isViewDidAppear)
  594. {
  595. isMark = NO;
  596. }
  597. }
  598. return isMark;
  599. }
  600. /*!
  601. @method
  602. @brief 位置消息被点击选择
  603. @discussion
  604. @param model 消息model
  605. @result
  606. */
  607. - (void)_locationMessageCellSelected:(id<IMessageModel>)model
  608. {
  609. // _scrollToBottomWhenAppear = NO;
  610. // EaseLocationViewController *locationController = [[EaseLocationViewController alloc] initWithLocation:CLLocationCoordinate2DMake(model.latitude, model.longitude)];
  611. // [self.navigationController pushViewController:locationController animated:YES];
  612. }
  613. /*!
  614. @method
  615. @brief 视频消息被点击选择
  616. @discussion
  617. @param model 消息model
  618. @result
  619. */
  620. - (void)_videoMessageCellSelected:(id<IMessageModel>)model
  621. {
  622. _scrollToBottomWhenAppear = NO;
  623. EMVideoMessageBody *videoBody = (EMVideoMessageBody*)model.message.body;
  624. NSString *localPath = [model.fileLocalPath length] > 0 ? model.fileLocalPath : videoBody.localPath;
  625. if ([localPath length] == 0) {
  626. [self showHint:NSEaseLocalizedString(@"message.videoFail", @"video for failure!")];
  627. return;
  628. }
  629. dispatch_block_t block = ^{
  630. //send the acknowledgement
  631. [self _sendHasReadResponseForMessages:@[model.message]
  632. isRead:YES];
  633. NSURL *videoURL = [NSURL fileURLWithPath:localPath];
  634. MPMoviePlayerViewController *moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
  635. [moviePlayerController.moviePlayer prepareToPlay];
  636. moviePlayerController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
  637. [self presentMoviePlayerViewControllerAnimated:moviePlayerController];
  638. };
  639. __weak typeof(self) weakSelf = self;
  640. void (^completion)(EMMessage *aMessage, EMError *error) = ^(EMMessage *aMessage, EMError *error) {
  641. if (!error)
  642. {
  643. [weakSelf _reloadTableViewDataWithMessage:aMessage];
  644. }
  645. else
  646. {
  647. [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
  648. }
  649. };
  650. if (videoBody.thumbnailDownloadStatus == EMDownloadStatusFailed || ![[NSFileManager defaultManager] fileExistsAtPath:videoBody.thumbnailLocalPath]) {
  651. [self showHint:@"begin downloading thumbnail image, click later"];
  652. [[EMClient sharedClient].chatManager downloadMessageThumbnail:model.message progress:nil completion:completion];
  653. return;
  654. }
  655. if (videoBody.downloadStatus == EMDownloadStatusSuccessed && [[NSFileManager defaultManager] fileExistsAtPath:localPath])
  656. {
  657. block();
  658. return;
  659. }
  660. [self showHudInView:self.view hint:NSEaseLocalizedString(@"message.downloadingVideo", @"downloading video...")];
  661. [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
  662. [weakSelf hideHud];
  663. if (!error) {
  664. block();
  665. }else{
  666. [weakSelf showHint:NSEaseLocalizedString(@"message.videoFail", @"video for failure!")];
  667. }
  668. }];
  669. }
  670. /*!
  671. @method
  672. @brief 图片消息被点击选择
  673. @discussion
  674. @param model 消息model
  675. @result
  676. */
  677. - (void)_imageMessageCellSelected:(id<IMessageModel>)model
  678. {
  679. __weak EaseMessageViewController *weakSelf = self;
  680. EMImageMessageBody *imageBody = (EMImageMessageBody*)[model.message body];
  681. if ([imageBody type] == EMMessageBodyTypeImage) {
  682. if (imageBody.thumbnailDownloadStatus == EMDownloadStatusSuccessed) {
  683. if (imageBody.downloadStatus == EMDownloadStatusSuccessed)
  684. {
  685. //send the acknowledgement
  686. [weakSelf _sendHasReadResponseForMessages:@[model.message] isRead:YES];
  687. NSString *localPath = model.message == nil ? model.fileLocalPath : [imageBody localPath];
  688. if (localPath && localPath.length > 0) {
  689. UIImage *image = [UIImage imageWithContentsOfFile:localPath];
  690. if (image) {
  691. [[EaseMessageReadManager defaultManager] showBrowserWithImages:@[image]];
  692. return;
  693. }
  694. }
  695. }
  696. [weakSelf showHudInView:weakSelf.view hint:NSEaseLocalizedString(@"message.downloadingImage", @"downloading a image...")];
  697. [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
  698. [weakSelf hideHud];
  699. if (!error) {
  700. //send the acknowledgement
  701. [weakSelf _sendHasReadResponseForMessages:@[model.message] isRead:YES];
  702. NSString *localPath = message == nil ? model.fileLocalPath : [(EMImageMessageBody*)message.body localPath];
  703. if (localPath && localPath.length > 0) {
  704. UIImage *image = [UIImage imageWithContentsOfFile:localPath];
  705. // weakSelf.isScrollToBottom = NO;
  706. if (image)
  707. {
  708. [[EaseMessageReadManager defaultManager] showBrowserWithImages:@[image]];
  709. }
  710. else
  711. {
  712. NSLog(@"Read %@ failed!", localPath);
  713. }
  714. return ;
  715. }
  716. }
  717. [weakSelf showHint:NSEaseLocalizedString(@"message.imageFail", @"image for failure!")];
  718. }];
  719. }else{
  720. //get the message thumbnail
  721. [[EMClient sharedClient].chatManager downloadMessageThumbnail:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
  722. if (!error) {
  723. [weakSelf _reloadTableViewDataWithMessage:model.message];
  724. }else{
  725. [weakSelf showHint:NSEaseLocalizedString(@"message.thumImageFail", @"thumbnail for failure!")];
  726. }
  727. }];
  728. }
  729. }
  730. }
  731. /*!
  732. @method
  733. @brief 语音消息被点击选择
  734. @discussion
  735. @param model 消息model
  736. @result
  737. */
  738. - (void)_audioMessageCellSelected:(id<IMessageModel>)model
  739. {
  740. _scrollToBottomWhenAppear = NO;
  741. EMVoiceMessageBody *body = (EMVoiceMessageBody*)model.message.body;
  742. EMDownloadStatus downloadStatus = [body downloadStatus];
  743. if (downloadStatus == EMDownloadStatusDownloading) {
  744. [self showHint:NSEaseLocalizedString(@"message.downloadingAudio", @"downloading voice, click later")];
  745. return;
  746. }
  747. else if (downloadStatus == EMDownloadStatusFailed)
  748. {
  749. [self showHint:NSEaseLocalizedString(@"message.downloadingAudio", @"downloading voice, click later")];
  750. [[EMClient sharedClient].chatManager downloadMessageAttachment:model.message progress:nil completion:nil];
  751. return;
  752. }
  753. // play the audio
  754. if (model.bodyType == EMMessageBodyTypeVoice) {
  755. //send the acknowledgement
  756. [self _sendHasReadResponseForMessages:@[model.message] isRead:YES];
  757. __weak EaseMessageViewController *weakSelf = self;
  758. BOOL isPrepare = [[EaseMessageReadManager defaultManager] prepareMessageAudioModel:model updateViewCompletion:^(EaseMessageModel *prevAudioModel, EaseMessageModel *currentAudioModel) {
  759. if (prevAudioModel || currentAudioModel) {
  760. [weakSelf.tableView reloadData];
  761. }
  762. }];
  763. if (isPrepare) {
  764. _isPlayingAudio = YES;
  765. __weak EaseMessageViewController *weakSelf = self;
  766. [[EMCDDeviceManager sharedInstance] enableProximitySensor];
  767. [[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:model.fileLocalPath completion:^(NSError *error) {
  768. [[EaseMessageReadManager defaultManager] stopMessageAudioModel];
  769. dispatch_async(dispatch_get_main_queue(), ^{
  770. [weakSelf.tableView reloadData];
  771. weakSelf.isPlayingAudio = NO;
  772. [[EMCDDeviceManager sharedInstance] disableProximitySensor];
  773. });
  774. }];
  775. }
  776. else{
  777. _isPlayingAudio = NO;
  778. }
  779. }
  780. }
  781. #pragma mark - pivate data
  782. /*!
  783. @method
  784. @brief 加载历史消息
  785. @discussion
  786. @param messageId 参考消息的ID
  787. @param count 获取条数
  788. @param isAppend 是否在dataArray直接添加
  789. @result
  790. */
  791. - (void)_loadMessagesBefore:(NSString*)messageId
  792. count:(NSInteger)count
  793. append:(BOOL)isAppend
  794. {
  795. __weak typeof(self) weakSelf = self;
  796. void (^refresh)(NSArray *messages) = ^(NSArray *messages) {
  797. dispatch_async(_messageQueue, ^{
  798. //Format the message
  799. NSArray *formattedMessages = [weakSelf formatMessages:messages];
  800. //Refresh the page
  801. dispatch_async(dispatch_get_main_queue(), ^{
  802. EaseMessageViewController *strongSelf = weakSelf;
  803. if (strongSelf) {
  804. NSInteger scrollToIndex = 0;
  805. if (isAppend) {
  806. [strongSelf.messsagesSource insertObjects:messages atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [messages count])]];
  807. //Combine the message
  808. id object = [strongSelf.dataArray firstObject];
  809. if ([object isKindOfClass:[NSString class]]) {
  810. NSString *timestamp = object;
  811. [formattedMessages enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id model, NSUInteger idx, BOOL *stop) {
  812. if ([model isKindOfClass:[NSString class]] && [timestamp isEqualToString:model]) {
  813. [strongSelf.dataArray removeObjectAtIndex:0];
  814. *stop = YES;
  815. }
  816. }];
  817. }
  818. scrollToIndex = [strongSelf.dataArray count];
  819. [strongSelf.dataArray insertObjects:formattedMessages atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [formattedMessages count])]];
  820. }
  821. else {
  822. [strongSelf.messsagesSource removeAllObjects];
  823. [strongSelf.messsagesSource addObjectsFromArray:messages];
  824. [strongSelf.dataArray removeAllObjects];
  825. [strongSelf.dataArray addObjectsFromArray:formattedMessages];
  826. }
  827. EMMessage *latest = [strongSelf.messsagesSource lastObject];
  828. strongSelf.messageTimeIntervalTag = latest.timestamp;
  829. [strongSelf.tableView reloadData];
  830. [strongSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - scrollToIndex - 1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
  831. }
  832. });
  833. //re-download all messages that are not successfully downloaded
  834. for (EMMessage *message in messages)
  835. {
  836. [weakSelf _downloadMessageAttachments:message];
  837. }
  838. //send the read acknoledgement
  839. [weakSelf _sendHasReadResponseForMessages:messages
  840. isRead:NO];
  841. });
  842. };
  843. [self.conversation loadMessagesStartFromId:messageId count:(int)count searchDirection:EMMessageSearchDirectionUp completion:^(NSArray *aMessages, EMError *aError) {
  844. if (!aError && [aMessages count]) {
  845. refresh(aMessages);
  846. }
  847. }];
  848. }
  849. #pragma mark - GestureRecognizer
  850. -(void)keyBoardHidden:(UITapGestureRecognizer *)tapRecognizer
  851. {
  852. if (tapRecognizer.state == UIGestureRecognizerStateEnded) {
  853. [self.chatToolbar endEditing:YES];
  854. }
  855. }
  856. - (void)handleLongPress:(UILongPressGestureRecognizer *)recognizer
  857. {
  858. if (recognizer.state == UIGestureRecognizerStateBegan && [self.dataArray count] > 0)
  859. {
  860. CGPoint location = [recognizer locationInView:self.tableView];
  861. NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint:location];
  862. BOOL canLongPress = NO;
  863. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:canLongPressRowAtIndexPath:)]) {
  864. canLongPress = [_dataSource messageViewController:self
  865. canLongPressRowAtIndexPath:indexPath];
  866. }
  867. if (!canLongPress) {
  868. return;
  869. }
  870. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:didLongPressRowAtIndexPath:)]) {
  871. [_dataSource messageViewController:self
  872. didLongPressRowAtIndexPath:indexPath];
  873. }
  874. else{
  875. id object = [self.dataArray objectAtIndex:indexPath.row];
  876. if (![object isKindOfClass:[NSString class]]) {
  877. EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
  878. [cell becomeFirstResponder];
  879. _menuIndexPath = indexPath;
  880. [self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
  881. }
  882. }
  883. }
  884. }
  885. #pragma mark - Table view data source
  886. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  887. {
  888. // Return the number of sections.
  889. return 1;
  890. }
  891. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  892. {
  893. // Return the number of rows in the section.
  894. return [self.dataArray count];
  895. }
  896. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  897. {
  898. id object = [self.dataArray objectAtIndex:indexPath.row];
  899. //time cell
  900. if ([object isKindOfClass:[NSString class]]) {
  901. NSString *TimeCellIdentifier = [EaseMessageTimeCell cellIdentifier];
  902. EaseMessageTimeCell *timeCell = (EaseMessageTimeCell *)[tableView dequeueReusableCellWithIdentifier:TimeCellIdentifier];
  903. if (timeCell == nil) {
  904. timeCell = [[EaseMessageTimeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TimeCellIdentifier];
  905. timeCell.selectionStyle = UITableViewCellSelectionStyleNone;
  906. }
  907. timeCell.title = object;
  908. return timeCell;
  909. }
  910. else{
  911. id<IMessageModel> model = object;
  912. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:cellForMessageModel:)]) {
  913. UITableViewCell *cell = [_delegate messageViewController:tableView cellForMessageModel:model];
  914. if (cell) {
  915. if ([cell isKindOfClass:[EaseMessageCell class]]) {
  916. EaseMessageCell *emcell= (EaseMessageCell*)cell;
  917. if (emcell.delegate == nil) {
  918. emcell.delegate = self;
  919. }
  920. }
  921. return cell;
  922. }
  923. }
  924. if (_dataSource && [_dataSource respondsToSelector:@selector(isEmotionMessageFormessageViewController:messageModel:)]) {
  925. BOOL flag = [_dataSource isEmotionMessageFormessageViewController:self messageModel:model];
  926. if (flag) {
  927. NSString *CellIdentifier = [EaseCustomMessageCell cellIdentifierWithModel:model];
  928. //send cell
  929. EaseCustomMessageCell *sendCell = (EaseCustomMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  930. // Configure the cell...
  931. if (sendCell == nil) {
  932. sendCell = [[EaseCustomMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
  933. sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
  934. }
  935. if (_dataSource && [_dataSource respondsToSelector:@selector(emotionURLFormessageViewController:messageModel:)]) {
  936. EaseEmotion *emotion = [_dataSource emotionURLFormessageViewController:self messageModel:model];
  937. if (emotion) {
  938. model.image = [UIImage sd_animatedGIFNamed:emotion.emotionOriginal];
  939. model.fileURLPath = emotion.emotionOriginalURL;
  940. }
  941. }
  942. sendCell.model = model;
  943. sendCell.delegate = self;
  944. return sendCell;
  945. }
  946. }
  947. NSString *CellIdentifier = [EaseMessageCell cellIdentifierWithModel:model];
  948. EaseBaseMessageCell *sendCell = (EaseBaseMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  949. // Configure the cell...
  950. if (sendCell == nil) {
  951. sendCell = [[EaseBaseMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
  952. sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
  953. sendCell.delegate = self;
  954. }
  955. sendCell.model = model;
  956. return sendCell;
  957. }
  958. }
  959. #pragma mark - Table view delegate
  960. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  961. {
  962. id object = [self.dataArray objectAtIndex:indexPath.row];
  963. if ([object isKindOfClass:[NSString class]]) {
  964. return self.timeCellHeight;
  965. }
  966. else{
  967. id<IMessageModel> model = object;
  968. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:heightForMessageModel:withCellWidth:)]) {
  969. CGFloat height = [_delegate messageViewController:self heightForMessageModel:model withCellWidth:tableView.frame.size.width];
  970. if (height) {
  971. return height;
  972. }
  973. }
  974. if (_dataSource && [_dataSource respondsToSelector:@selector(isEmotionMessageFormessageViewController:messageModel:)]) {
  975. BOOL flag = [_dataSource isEmotionMessageFormessageViewController:self messageModel:model];
  976. if (flag) {
  977. return [EaseCustomMessageCell cellHeightWithModel:model];
  978. }
  979. }
  980. return [EaseBaseMessageCell cellHeightWithModel:model];
  981. }
  982. }
  983. #pragma mark - UIImagePickerControllerDelegate
  984. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
  985. {
  986. NSString *mediaType = info[UIImagePickerControllerMediaType];
  987. if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
  988. NSURL *videoURL = info[UIImagePickerControllerMediaURL];
  989. // video url:
  990. // file:///private/var/mobile/Applications/B3CDD0B2-2F19-432B-9CFA-158700F4DE8F/tmp/capture-T0x16e39100.tmp.9R8weF/capturedvideo.mp4
  991. // we will convert it to mp4 format
  992. NSURL *mp4 = [self _convert2Mp4:videoURL];
  993. NSFileManager *fileman = [NSFileManager defaultManager];
  994. if ([fileman fileExistsAtPath:videoURL.path]) {
  995. NSError *error = nil;
  996. [fileman removeItemAtURL:videoURL error:&error];
  997. if (error) {
  998. NSLog(@"failed to remove file, error:%@.", error);
  999. }
  1000. }
  1001. [self sendVideoMessageWithURL:mp4];
  1002. }else{
  1003. NSURL *url = info[UIImagePickerControllerReferenceURL];
  1004. if (url == nil) {
  1005. UIImage *orgImage = info[UIImagePickerControllerOriginalImage];
  1006. [self sendImageMessage:orgImage];
  1007. } else {
  1008. if ([[UIDevice currentDevice].systemVersion doubleValue] >= 9.0f) {
  1009. PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];
  1010. [result enumerateObjectsUsingBlock:^(PHAsset *asset , NSUInteger idx, BOOL *stop){
  1011. if (asset) {
  1012. [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *data, NSString *uti, UIImageOrientation orientation, NSDictionary *dic){
  1013. if (data != nil) {
  1014. [self sendImageMessageWithData:data];
  1015. } else {
  1016. [self showHint:NSEaseLocalizedString(@"message.smallerImage", @"The image size is too large, please choose another one")];
  1017. }
  1018. }];
  1019. }
  1020. }];
  1021. } else {
  1022. ALAssetsLibrary *alasset = [[ALAssetsLibrary alloc] init];
  1023. [alasset assetForURL:url resultBlock:^(ALAsset *asset) {
  1024. if (asset) {
  1025. ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
  1026. Byte* buffer = (Byte*)malloc((size_t)[assetRepresentation size]);
  1027. NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil];
  1028. NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
  1029. [self sendImageMessageWithData:fileData];
  1030. }
  1031. } failureBlock:NULL];
  1032. }
  1033. }
  1034. }
  1035. [picker dismissViewControllerAnimated:YES completion:nil];
  1036. self.isViewDidAppear = YES;
  1037. [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
  1038. }
  1039. - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
  1040. {
  1041. [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
  1042. self.isViewDidAppear = YES;
  1043. [[EaseSDKHelper shareHelper] setIsShowingimagePicker:NO];
  1044. }
  1045. #pragma mark - EaseMessageCellDelegate
  1046. - (void)messageCellSelected:(id<IMessageModel>)model
  1047. {
  1048. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didSelectMessageModel:)]) {
  1049. BOOL flag = [_delegate messageViewController:self didSelectMessageModel:model];
  1050. if (flag) {
  1051. [self _sendHasReadResponseForMessages:@[model.message] isRead:YES];
  1052. return;
  1053. }
  1054. }
  1055. switch (model.bodyType) {
  1056. case EMMessageBodyTypeImage:
  1057. {
  1058. _scrollToBottomWhenAppear = NO;
  1059. [self _imageMessageCellSelected:model];
  1060. }
  1061. break;
  1062. case EMMessageBodyTypeLocation:
  1063. {
  1064. // [self _locationMessageCellSelected:model];
  1065. }
  1066. break;
  1067. case EMMessageBodyTypeVoice:
  1068. {
  1069. [self _audioMessageCellSelected:model];
  1070. }
  1071. break;
  1072. case EMMessageBodyTypeVideo:
  1073. {
  1074. // [self _videoMessageCellSelected:model];
  1075. }
  1076. break;
  1077. case EMMessageBodyTypeFile:
  1078. {
  1079. _scrollToBottomWhenAppear = NO;
  1080. [self showHint:@"Custom implementation!"];
  1081. }
  1082. break;
  1083. default:
  1084. break;
  1085. }
  1086. }
  1087. - (void)statusButtonSelcted:(id<IMessageModel>)model withMessageCell:(EaseMessageCell*)messageCell
  1088. {
  1089. if ((model.messageStatus != EMMessageStatusFailed) && (model.messageStatus != EMMessageStatusPending))
  1090. {
  1091. return;
  1092. }
  1093. __weak typeof(self) weakself = self;
  1094. [[[EMClient sharedClient] chatManager] resendMessage:model.message progress:nil completion:^(EMMessage *message, EMError *error) {
  1095. if (!error) {
  1096. [weakself _refreshAfterSentMessage:message];
  1097. }
  1098. else {
  1099. [weakself.tableView reloadData];
  1100. }
  1101. }];
  1102. [self.tableView reloadData];
  1103. }
  1104. - (void)avatarViewSelcted:(id<IMessageModel>)model
  1105. {
  1106. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didSelectAvatarMessageModel:)]) {
  1107. [_delegate messageViewController:self didSelectAvatarMessageModel:model];
  1108. return;
  1109. }
  1110. _scrollToBottomWhenAppear = NO;
  1111. }
  1112. #pragma mark - EMChatToolbarDelegate
  1113. - (void)chatToolbarDidChangeFrameToHeight:(CGFloat)toHeight
  1114. {
  1115. [UIView animateWithDuration:0.3 animations:^{
  1116. CGRect rect = self.tableView.frame;
  1117. rect.origin.y = 0;
  1118. rect.size.height = self.view.frame.size.height - toHeight;
  1119. self.tableView.frame = rect;
  1120. }];
  1121. [self _scrollViewToBottom:NO];
  1122. }
  1123. - (void)inputTextViewWillBeginEditing:(EaseTextView *)inputTextView
  1124. {
  1125. if (_menuController == nil) {
  1126. _menuController = [UIMenuController sharedMenuController];
  1127. }
  1128. [_menuController setMenuItems:nil];
  1129. }
  1130. - (void)didSendText:(NSString *)text
  1131. {
  1132. if (text && text.length > 0) {
  1133. [self sendTextMessage:text];
  1134. [self.atTargets removeAllObjects];
  1135. }
  1136. }
  1137. - (BOOL)didInputAtInLocation:(NSUInteger)location
  1138. {
  1139. if ([self.delegate respondsToSelector:@selector(messageViewController:selectAtTarget:)] && self.conversation.type == EMConversationTypeGroupChat) {
  1140. location += 1;
  1141. __weak typeof(self) weakSelf = self;
  1142. [self.delegate messageViewController:self selectAtTarget:^(EaseAtTarget *target) {
  1143. __strong EaseMessageViewController *strongSelf = weakSelf;
  1144. if (strongSelf && target) {
  1145. if ([target.userId length] || [target.nickname length]) {
  1146. [strongSelf.atTargets addObject:target];
  1147. NSString *insertStr = [NSString stringWithFormat:@"%@ ", target.nickname ? target.nickname : target.userId];
  1148. EaseChatToolbar *toolbar = (EaseChatToolbar*)strongSelf.chatToolbar;
  1149. NSMutableString *originStr = [toolbar.inputTextView.text mutableCopy];
  1150. NSUInteger insertLocation = location > originStr.length ? originStr.length : location;
  1151. [originStr insertString:insertStr atIndex:insertLocation];
  1152. toolbar.inputTextView.text = originStr;
  1153. toolbar.inputTextView.selectedRange = NSMakeRange(insertLocation + insertStr.length, 0);
  1154. [toolbar.inputTextView becomeFirstResponder];
  1155. }
  1156. }
  1157. else if (strongSelf) {
  1158. EaseChatToolbar *toolbar = (EaseChatToolbar*)strongSelf.chatToolbar;
  1159. [toolbar.inputTextView becomeFirstResponder];
  1160. }
  1161. }];
  1162. EaseChatToolbar *toolbar = (EaseChatToolbar*)self.chatToolbar;
  1163. toolbar.inputTextView.text = [NSString stringWithFormat:@"%@@", toolbar.inputTextView.text];
  1164. [toolbar.inputTextView resignFirstResponder];
  1165. return YES;
  1166. }
  1167. else {
  1168. return NO;
  1169. }
  1170. }
  1171. - (BOOL)didDeleteCharacterFromLocation:(NSUInteger)location
  1172. {
  1173. EaseChatToolbar *toolbar = (EaseChatToolbar*)self.chatToolbar;
  1174. if ([toolbar.inputTextView.text length] == location + 1) {
  1175. //delete last character
  1176. NSString *inputText = toolbar.inputTextView.text;
  1177. NSRange range = [inputText rangeOfString:@"@" options:NSBackwardsSearch];
  1178. if (range.location != NSNotFound) {
  1179. if (location - range.location > 1) {
  1180. NSString *sub = [inputText substringWithRange:NSMakeRange(range.location + 1, location - range.location - 1)];
  1181. for (EaseAtTarget *target in self.atTargets) {
  1182. if ([sub isEqualToString:target.userId] || [sub isEqualToString:target.nickname]) {
  1183. inputText = range.location > 0 ? [inputText substringToIndex:range.location] : @"";
  1184. toolbar.inputTextView.text = inputText;
  1185. toolbar.inputTextView.selectedRange = NSMakeRange(inputText.length, 0);
  1186. [self.atTargets removeObject:target];
  1187. return YES;
  1188. }
  1189. }
  1190. }
  1191. }
  1192. }
  1193. return NO;
  1194. }
  1195. - (void)didSendText:(NSString *)text withExt:(NSDictionary*)ext
  1196. {
  1197. if ([ext objectForKey:EASEUI_EMOTION_DEFAULT_EXT]) {
  1198. EaseEmotion *emotion = [ext objectForKey:EASEUI_EMOTION_DEFAULT_EXT];
  1199. if (self.dataSource && [self.dataSource respondsToSelector:@selector(emotionExtFormessageViewController:easeEmotion:)]) {
  1200. NSDictionary *ext = [self.dataSource emotionExtFormessageViewController:self easeEmotion:emotion];
  1201. [self sendTextMessage:emotion.emotionTitle withExt:ext];
  1202. } else {
  1203. [self sendTextMessage:emotion.emotionTitle withExt:@{MESSAGE_ATTR_EXPRESSION_ID:emotion.emotionId,MESSAGE_ATTR_IS_BIG_EXPRESSION:@(YES)}];
  1204. }
  1205. return;
  1206. }
  1207. if (text && text.length > 0) {
  1208. [self sendTextMessage:text withExt:ext];
  1209. }
  1210. }
  1211. - (void)didStartRecordingVoiceAction:(UIView *)recordView
  1212. {
  1213. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
  1214. [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchDown];
  1215. } else {
  1216. if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
  1217. [(EaseRecordView *)self.recordView recordButtonTouchDown];
  1218. }
  1219. }
  1220. [self _canRecordComplation:^(EMRecordResponse recordResponse) {
  1221. switch (recordResponse) {
  1222. case EMRequestRecord:
  1223. break;
  1224. case EMCanRecord:
  1225. {
  1226. _isRecording = YES;
  1227. EaseRecordView *tmpView = (EaseRecordView *)recordView;
  1228. tmpView.center = self.view.center;
  1229. [self.view addSubview:tmpView];
  1230. [self.view bringSubviewToFront:recordView];
  1231. int x = arc4random() % 100000;
  1232. NSTimeInterval time = [[NSDate date] timeIntervalSince1970];
  1233. NSString *fileName = [NSString stringWithFormat:@"%d%d",(int)time,x];
  1234. [[EMCDDeviceManager sharedInstance] asyncStartRecordingWithFileName:fileName completion:^(NSError *error)
  1235. {
  1236. if (error) {
  1237. NSLog(@"%@",NSEaseLocalizedString(@"message.startRecordFail", @"failure to start recording"));
  1238. _isRecording = NO;
  1239. }
  1240. }];
  1241. }
  1242. break;
  1243. case EMCanNotRecord:
  1244. {
  1245. UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"record.failToPermission", @"No recording permission") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil];
  1246. [alertView show];
  1247. }
  1248. break;
  1249. default:
  1250. break;
  1251. }
  1252. }];
  1253. }
  1254. - (void)didCancelRecordingVoiceAction:(UIView *)recordView
  1255. {
  1256. if(_isRecording) {
  1257. [[EMCDDeviceManager sharedInstance] cancelCurrentRecording];
  1258. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
  1259. [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchUpOutside];
  1260. } else {
  1261. if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
  1262. [(EaseRecordView *)self.recordView recordButtonTouchUpOutside];
  1263. }
  1264. [self.recordView removeFromSuperview];
  1265. }
  1266. _isRecording = NO;
  1267. }
  1268. }
  1269. - (void)didFinishRecoingVoiceAction:(UIView *)recordView
  1270. {
  1271. if (_isRecording) {
  1272. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
  1273. [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeTouchUpInside];
  1274. } else {
  1275. if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
  1276. [(EaseRecordView *)self.recordView recordButtonTouchUpInside];
  1277. }
  1278. [self.recordView removeFromSuperview];
  1279. }
  1280. __weak typeof(self) weakSelf = self;
  1281. [[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
  1282. if (!error) {
  1283. [weakSelf sendVoiceMessageWithLocalPath:recordPath duration:aDuration];
  1284. }
  1285. else {
  1286. [weakSelf showHudInView:self.view hint:error.domain];
  1287. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1288. [weakSelf hideHud];
  1289. });
  1290. }
  1291. }];
  1292. _isRecording = NO;
  1293. }
  1294. }
  1295. - (void)didDragInsideAction:(UIView *)recordView
  1296. {
  1297. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
  1298. [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeDragInside];
  1299. } else {
  1300. if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
  1301. [(EaseRecordView *)self.recordView recordButtonDragInside];
  1302. }
  1303. }
  1304. }
  1305. - (void)didDragOutsideAction:(UIView *)recordView
  1306. {
  1307. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectRecordView:withEvenType:)]) {
  1308. [self.delegate messageViewController:self didSelectRecordView:recordView withEvenType:EaseRecordViewTypeDragOutside];
  1309. } else {
  1310. if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
  1311. [(EaseRecordView *)self.recordView recordButtonDragOutside];
  1312. }
  1313. }
  1314. }
  1315. #pragma mark - EaseChatBarMoreViewDelegate
  1316. - (void)moreView:(EaseChatBarMoreView *)moreView didItemInMoreViewAtIndex:(NSInteger)index
  1317. {
  1318. if ([self.delegate respondsToSelector:@selector(messageViewController:didSelectMoreView:AtIndex:)]) {
  1319. [self.delegate messageViewController:self didSelectMoreView:moreView AtIndex:index];
  1320. return;
  1321. }
  1322. }
  1323. - (void)moreViewPhotoAction:(EaseChatBarMoreView *)moreView
  1324. {
  1325. // Hide the keyboard
  1326. [self.chatToolbar endEditing:YES];
  1327. // Pop image picker
  1328. self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  1329. self.imagePicker.mediaTypes = @[(NSString *)kUTTypeImage];
  1330. [self presentViewController:self.imagePicker animated:YES completion:NULL];
  1331. self.isViewDidAppear = NO;
  1332. [[EaseSDKHelper shareHelper] setIsShowingimagePicker:YES];
  1333. }
  1334. - (void)moreViewTakePicAction:(EaseChatBarMoreView *)moreView
  1335. {
  1336. // Hide the keyboard
  1337. [self.chatToolbar endEditing:YES];
  1338. #if TARGET_IPHONE_SIMULATOR
  1339. [self showHint:NSEaseLocalizedString(@"message.simulatorNotSupportCamera", @"simulator does not support taking picture")];
  1340. #elif TARGET_OS_IPHONE
  1341. self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
  1342. self.imagePicker.mediaTypes = @[(NSString *)kUTTypeImage,(NSString *)kUTTypeMovie];
  1343. [self presentViewController:self.imagePicker animated:YES completion:NULL];
  1344. self.isViewDidAppear = NO;
  1345. [[EaseSDKHelper shareHelper] setIsShowingimagePicker:YES];
  1346. #endif
  1347. }
  1348. - (void)moreViewLocationAction:(EaseChatBarMoreView *)moreView
  1349. {
  1350. // Hide the keyboard
  1351. [self.chatToolbar endEditing:YES];
  1352. // EaseLocationViewController *locationController = [[EaseLocationViewController alloc] init];
  1353. // locationController.delegate = self;
  1354. // [self.navigationController pushViewController:locationController animated:YES];
  1355. }
  1356. - (void)moreViewAudioCallAction:(EaseChatBarMoreView *)moreView
  1357. {
  1358. // Hide the keyboard
  1359. [self.chatToolbar endEditing:YES];
  1360. // [[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_CALL object:@{@"chatter":self.conversation.conversationId, @"type":[NSNumber numberWithInt:0]}];
  1361. }
  1362. - (void)moreViewVideoCallAction:(EaseChatBarMoreView *)moreView
  1363. {
  1364. // Hide the keyboard
  1365. [self.chatToolbar endEditing:YES];
  1366. // [[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_CALL object:@{@"chatter":self.conversation.conversationId, @"type":[NSNumber numberWithInt:1]}];
  1367. }
  1368. #pragma mark - EMLocationViewDelegate
  1369. -(void)sendLocationLatitude:(double)latitude
  1370. longitude:(double)longitude
  1371. andAddress:(NSString *)address
  1372. {
  1373. // [self sendLocationMessageLatitude:latitude longitude:longitude andAddress:address];
  1374. }
  1375. #pragma mark - Hyphenate
  1376. #pragma mark - EMChatManagerDelegate
  1377. - (void)didReceiveMessages:(NSArray *)aMessages
  1378. {
  1379. for (EMMessage *message in aMessages) {
  1380. if ([self.conversation.conversationId isEqualToString:message.conversationId]) {
  1381. [self addMessageToDataSource:message progress:nil];
  1382. [self _sendHasReadResponseForMessages:@[message]
  1383. isRead:NO];
  1384. if ([self _shouldMarkMessageAsRead])
  1385. {
  1386. [self.conversation markMessageAsReadWithId:message.messageId error:nil];
  1387. }
  1388. }
  1389. }
  1390. }
  1391. - (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages
  1392. {
  1393. for (EMMessage *message in aCmdMessages) {
  1394. if ([self.conversation.conversationId isEqualToString:message.conversationId]) {
  1395. [self showHint:NSEaseLocalizedString(@"receiveCmd", @"receive cmd message")];
  1396. break;
  1397. }
  1398. }
  1399. }
  1400. - (void)didReceiveCmdMessages:(NSArray *)aCmdMessages
  1401. {
  1402. for (EMMessage *message in aCmdMessages) {
  1403. if ([self.conversation.conversationId isEqualToString:message.conversationId]) {
  1404. [self showHint:NSEaseLocalizedString(@"receiveCmd", @"receive cmd message")];
  1405. break;
  1406. }
  1407. }
  1408. }
  1409. - (void)messagesDidDeliver:(NSArray *)aMessages
  1410. {
  1411. for(EMMessage *message in aMessages){
  1412. [self _updateMessageStatus:message];
  1413. }
  1414. }
  1415. - (void)didReceiveHasDeliveredAcks:(NSArray *)aMessages
  1416. {
  1417. for(EMMessage *message in aMessages){
  1418. [self _updateMessageStatus:message];
  1419. }
  1420. }
  1421. - (void)messagesDidRead:(NSArray *)aMessages
  1422. {
  1423. for (EMMessage *message in aMessages) {
  1424. if (![self.conversation.conversationId isEqualToString:message.conversationId]){
  1425. continue;
  1426. }
  1427. __block id<IMessageModel> model = nil;
  1428. __block BOOL isHave = NO;
  1429. [self.dataArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
  1430. {
  1431. if ([obj conformsToProtocol:@protocol(IMessageModel)])
  1432. {
  1433. model = (id<IMessageModel>)obj;
  1434. if ([model.messageId isEqualToString:message.messageId])
  1435. {
  1436. model.message.isReadAcked = YES;
  1437. isHave = YES;
  1438. *stop = YES;
  1439. }
  1440. }
  1441. }];
  1442. if(!isHave){
  1443. return;
  1444. }
  1445. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didReceiveHasReadAckForModel:)]) {
  1446. [_delegate messageViewController:self didReceiveHasReadAckForModel:model];
  1447. }
  1448. else{
  1449. [self.tableView reloadData];
  1450. }
  1451. }
  1452. }
  1453. - (void)didReceiveHasReadAcks:(NSArray *)aMessages
  1454. {
  1455. for (EMMessage *message in aMessages) {
  1456. if (![self.conversation.conversationId isEqualToString:message.conversationId]){
  1457. continue;
  1458. }
  1459. __block id<IMessageModel> model = nil;
  1460. __block BOOL isHave = NO;
  1461. [self.dataArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
  1462. {
  1463. if ([obj conformsToProtocol:@protocol(IMessageModel)])
  1464. {
  1465. model = (id<IMessageModel>)obj;
  1466. if ([model.messageId isEqualToString:message.messageId])
  1467. {
  1468. model.message.isReadAcked = YES;
  1469. isHave = YES;
  1470. *stop = YES;
  1471. }
  1472. }
  1473. }];
  1474. if(!isHave){
  1475. return;
  1476. }
  1477. if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:didReceiveHasReadAckForModel:)]) {
  1478. [_delegate messageViewController:self didReceiveHasReadAckForModel:model];
  1479. }
  1480. else{
  1481. [self.tableView reloadData];
  1482. }
  1483. }
  1484. }
  1485. - (void)messageStatusDidChange:(EMMessage *)aMessage
  1486. error:(EMError *)aError
  1487. {
  1488. [self _updateMessageStatus:aMessage];
  1489. }
  1490. - (void)didMessageStatusChanged:(EMMessage *)aMessage
  1491. error:(EMError *)aError;
  1492. {
  1493. [self _updateMessageStatus:aMessage];
  1494. }
  1495. - (void)messageAttachmentStatusDidChange:(EMMessage *)aMessage
  1496. error:(EMError *)aError
  1497. {
  1498. if (!ferror) {
  1499. EMFileMessageBody *fileBody = (EMFileMessageBody*)[aMessage body];
  1500. if ([fileBody type] == EMMessageBodyTypeImage) {
  1501. EMImageMessageBody *imageBody = (EMImageMessageBody *)fileBody;
  1502. if ([imageBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
  1503. {
  1504. [self _reloadTableViewDataWithMessage:aMessage];
  1505. }
  1506. }else if([fileBody type] == EMMessageBodyTypeVideo){
  1507. EMVideoMessageBody *videoBody = (EMVideoMessageBody *)fileBody;
  1508. if ([videoBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
  1509. {
  1510. [self _reloadTableViewDataWithMessage:aMessage];
  1511. }
  1512. }else if([fileBody type] == EMMessageBodyTypeVoice){
  1513. if ([fileBody downloadStatus] == EMDownloadStatusSuccessed)
  1514. {
  1515. [self _reloadTableViewDataWithMessage:aMessage];
  1516. }
  1517. }
  1518. }else{
  1519. }
  1520. }
  1521. - (void)didMessageAttachmentsStatusChanged:(EMMessage *)message
  1522. error:(EMError *)error{
  1523. if (!error) {
  1524. EMFileMessageBody *fileBody = (EMFileMessageBody*)[message body];
  1525. if ([fileBody type] == EMMessageBodyTypeImage) {
  1526. EMImageMessageBody *imageBody = (EMImageMessageBody *)fileBody;
  1527. if ([imageBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
  1528. {
  1529. [self _reloadTableViewDataWithMessage:message];
  1530. }
  1531. }else if([fileBody type] == EMMessageBodyTypeVideo){
  1532. EMVideoMessageBody *videoBody = (EMVideoMessageBody *)fileBody;
  1533. if ([videoBody thumbnailDownloadStatus] == EMDownloadStatusSuccessed)
  1534. {
  1535. [self _reloadTableViewDataWithMessage:message];
  1536. }
  1537. }else if([fileBody type] == EMMessageBodyTypeVoice){
  1538. if ([fileBody downloadStatus] == EMDownloadStatusSuccessed)
  1539. {
  1540. [self _reloadTableViewDataWithMessage:message];
  1541. }
  1542. }
  1543. }else{
  1544. }
  1545. }
  1546. #pragma mark - EMCDDeviceManagerProximitySensorDelegate
  1547. - (void)proximitySensorChanged:(BOOL)isCloseToUser
  1548. {
  1549. AVAudioSession *audioSession = [AVAudioSession sharedInstance];
  1550. if (isCloseToUser)
  1551. {
  1552. [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
  1553. } else {
  1554. [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
  1555. if (self.playingVoiceModel == nil) {
  1556. [[EMCDDeviceManager sharedInstance] disableProximitySensor];
  1557. }
  1558. }
  1559. [audioSession setActive:YES error:nil];
  1560. }
  1561. #pragma mark - action
  1562. - (void)copyMenuAction:(id)sender
  1563. {
  1564. UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
  1565. if (self.menuIndexPath && self.menuIndexPath.row > 0) {
  1566. id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
  1567. pasteboard.string = model.text;
  1568. }
  1569. self.menuIndexPath = nil;
  1570. }
  1571. - (void)deleteMenuAction:(id)sender
  1572. {
  1573. if (self.menuIndexPath && self.menuIndexPath.row > 0) {
  1574. id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
  1575. NSMutableIndexSet *indexs = [NSMutableIndexSet indexSetWithIndex:self.menuIndexPath.row];
  1576. NSMutableArray *indexPaths = [NSMutableArray arrayWithObjects:self.menuIndexPath, nil];
  1577. [self.conversation deleteMessageWithId:model.message.messageId error:nil];
  1578. [self.messsagesSource removeObject:model.message];
  1579. if (self.menuIndexPath.row - 1 >= 0) {
  1580. id nextMessage = nil;
  1581. id prevMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row - 1)];
  1582. if (self.menuIndexPath.row + 1 < [self.dataArray count]) {
  1583. nextMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row + 1)];
  1584. }
  1585. if ((!nextMessage || [nextMessage isKindOfClass:[NSString class]]) && [prevMessage isKindOfClass:[NSString class]]) {
  1586. [indexs addIndex:self.menuIndexPath.row - 1];
  1587. [indexPaths addObject:[NSIndexPath indexPathForRow:(self.menuIndexPath.row - 1) inSection:0]];
  1588. }
  1589. }
  1590. [self.dataArray removeObjectsAtIndexes:indexs];
  1591. [self.tableView beginUpdates];
  1592. [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
  1593. [self.tableView endUpdates];
  1594. }
  1595. self.menuIndexPath = nil;
  1596. }
  1597. #pragma mark - public
  1598. - (NSArray *)formatMessages:(NSArray *)messages
  1599. {
  1600. NSMutableArray *formattedArray = [[NSMutableArray alloc] init];
  1601. if ([messages count] == 0) {
  1602. return formattedArray;
  1603. }
  1604. for (EMMessage *message in messages) {
  1605. //Calculate time interval
  1606. CGFloat interval = (self.messageTimeIntervalTag - message.timestamp) / 1000;
  1607. if (self.messageTimeIntervalTag < 0 || interval > 60 || interval < -60) {
  1608. NSDate *messageDate = [NSDate dateWithTimeIntervalInMilliSecondSince1970:(NSTimeInterval)message.timestamp];
  1609. NSString *timeStr = @"";
  1610. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:stringForDate:)]) {
  1611. timeStr = [_dataSource messageViewController:self stringForDate:messageDate];
  1612. }
  1613. else{
  1614. timeStr = [messageDate formattedTime];
  1615. }
  1616. [formattedArray addObject:timeStr];
  1617. self.messageTimeIntervalTag = message.timestamp;
  1618. }
  1619. //Construct message model
  1620. id<IMessageModel> model = nil;
  1621. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
  1622. model = [_dataSource messageViewController:self modelForMessage:message];
  1623. }
  1624. else{
  1625. // model = [self messageViewController:self modelForMessage:message];
  1626. model = [[EaseMessageModel alloc] initWithMessage:message];
  1627. model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
  1628. model.failImageName = @"imageDownloadFail";
  1629. NSLog(@"%@",model);
  1630. }
  1631. if (model) {
  1632. [formattedArray addObject:model];
  1633. }
  1634. }
  1635. return formattedArray;
  1636. }
  1637. -(void)addMessageToDataSource:(EMMessage *)message
  1638. progress:(id)progress
  1639. {
  1640. [self.messsagesSource addObject:message];
  1641. __weak EaseMessageViewController *weakSelf = self;
  1642. dispatch_async(_messageQueue, ^{
  1643. NSArray *messages = [weakSelf formatMessages:@[message]];
  1644. dispatch_async(dispatch_get_main_queue(), ^{
  1645. [weakSelf.dataArray addObjectsFromArray:messages];
  1646. [weakSelf.tableView reloadData];
  1647. [weakSelf.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[weakSelf.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  1648. });
  1649. });
  1650. }
  1651. #pragma mark - public
  1652. - (void)tableViewDidTriggerHeaderRefresh
  1653. {
  1654. self.messageTimeIntervalTag = -1;
  1655. NSString *messageId = nil;
  1656. if ([self.messsagesSource count] > 0) {
  1657. messageId = [(EMMessage *)self.messsagesSource.firstObject messageId];
  1658. }
  1659. else {
  1660. messageId = nil;
  1661. }
  1662. [self _loadMessagesBefore:messageId count:self.messageCountOfPage append:YES];
  1663. [self tableViewDidFinishTriggerHeader:YES reload:YES];
  1664. }
  1665. #pragma mark - send message
  1666. - (void)_refreshAfterSentMessage:(EMMessage*)aMessage
  1667. {
  1668. if ([self.messsagesSource count] && [EMClient sharedClient].options.sortMessageByServerTime) {
  1669. NSString *msgId = aMessage.messageId;
  1670. EMMessage *last = self.messsagesSource.lastObject;
  1671. if ([last isKindOfClass:[EMMessage class]]) {
  1672. __block NSUInteger index = NSNotFound;
  1673. index = NSNotFound;
  1674. [self.messsagesSource enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(EMMessage *obj, NSUInteger idx, BOOL *stop) {
  1675. if ([obj isKindOfClass:[EMMessage class]] && [obj.messageId isEqualToString:msgId]) {
  1676. index = idx;
  1677. *stop = YES;
  1678. }
  1679. }];
  1680. if (index != NSNotFound) {
  1681. [self.messsagesSource removeObjectAtIndex:index];
  1682. [self.messsagesSource addObject:aMessage];
  1683. //格式化消息
  1684. self.messageTimeIntervalTag = -1;
  1685. NSArray *formattedMessages = [self formatMessages:self.messsagesSource];
  1686. [self.dataArray removeAllObjects];
  1687. [self.dataArray addObjectsFromArray:formattedMessages];
  1688. [self.tableView reloadData];
  1689. [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.dataArray count] - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  1690. return;
  1691. }
  1692. }
  1693. }
  1694. [self.tableView reloadData];
  1695. }
  1696. - (void)_sendMessage:(EMMessage *)message
  1697. {
  1698. if (self.conversation.type == EMConversationTypeGroupChat){
  1699. message.chatType = EMChatTypeGroupChat;
  1700. }
  1701. else if (self.conversation.type == EMConversationTypeChatRoom){
  1702. message.chatType = EMChatTypeChatRoom;
  1703. }
  1704. NSString *uesrname = [NewUtils userDefaultsStringKey:NewUsername];
  1705. NSString *userheadUrl = [NewUtils userDefaultsStringKey:NewHXheadUrl];
  1706. //罗云飞自定义
  1707. if (uesrname != nil && userheadUrl != nil) {
  1708. }else{
  1709. if (userheadUrl == nil) {
  1710. userheadUrl = @"";
  1711. }else if (uesrname == nil){
  1712. uesrname = @"";
  1713. }else{
  1714. NSDictionary *exit = @{@"userAvatar":userheadUrl,@"userNick":uesrname};
  1715. if (message.ext != nil) {
  1716. }else{
  1717. message.ext = exit;
  1718. }
  1719. }
  1720. }
  1721. [self addMessageToDataSource:message
  1722. progress:nil];
  1723. __weak typeof(self) weakself = self;
  1724. [[EMClient sharedClient].chatManager sendMessage:message progress:^(int progress) {
  1725. if (weakself.dataSource && [weakself.dataSource respondsToSelector:@selector(messageViewController:updateProgress:messageModel:messageBody:)]) {
  1726. [weakself.dataSource messageViewController:weakself updateProgress:progress messageModel:nil messageBody:message.body];
  1727. }
  1728. } completion:^(EMMessage *aMessage, EMError *aError) {
  1729. if (!aError) {
  1730. [weakself _refreshAfterSentMessage:aMessage];
  1731. }
  1732. else {
  1733. [weakself.tableView reloadData];
  1734. }
  1735. }];
  1736. }
  1737. - (void)sendTextMessage:(NSString *)text
  1738. {
  1739. NSDictionary *ext = nil;
  1740. if (self.conversation.type == EMConversationTypeGroupChat) {
  1741. NSArray *targets = [self _searchAtTargets:text];
  1742. if ([targets count]) {
  1743. __block BOOL atAll = NO;
  1744. [targets enumerateObjectsUsingBlock:^(NSString *target, NSUInteger idx, BOOL *stop) {
  1745. if ([target compare:kGroupMessageAtAll options:NSCaseInsensitiveSearch] == NSOrderedSame) {
  1746. atAll = YES;
  1747. *stop = YES;
  1748. }
  1749. }];
  1750. if (atAll) {
  1751. ext = @{kGroupMessageAtList: kGroupMessageAtAll};
  1752. }
  1753. else {
  1754. ext = @{kGroupMessageAtList: targets};
  1755. }
  1756. }
  1757. }
  1758. [self sendTextMessage:text withExt:ext];
  1759. }
  1760. - (void)sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext
  1761. {
  1762. EMMessage *message = [EaseSDKHelper sendTextMessage:text
  1763. to:self.conversation.conversationId
  1764. messageType:[self _messageTypeFromConversationType]
  1765. messageExt:ext];
  1766. [self _sendMessage:message];
  1767. }
  1768. - (void)sendLocationMessageLatitude:(double)latitude
  1769. longitude:(double)longitude
  1770. andAddress:(NSString *)address
  1771. {
  1772. EMMessage *message = [EaseSDKHelper sendLocationMessageWithLatitude:latitude
  1773. longitude:longitude
  1774. address:address
  1775. to:self.conversation.conversationId
  1776. messageType:[self _messageTypeFromConversationType]
  1777. messageExt:nil];
  1778. [self _sendMessage:message];
  1779. }
  1780. - (void)sendImageMessageWithData:(NSData *)imageData
  1781. {
  1782. id progress = nil;
  1783. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
  1784. progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
  1785. }
  1786. else{
  1787. progress = self;
  1788. }
  1789. EMMessage *message = [EaseSDKHelper sendImageMessageWithImageData:imageData
  1790. to:self.conversation.conversationId
  1791. messageType:[self _messageTypeFromConversationType]
  1792. messageExt:nil];
  1793. [self _sendMessage:message];
  1794. }
  1795. - (void)sendImageMessage:(UIImage *)image
  1796. {
  1797. id progress = nil;
  1798. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
  1799. progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeImage];
  1800. }
  1801. else{
  1802. progress = self;
  1803. }
  1804. EMMessage *message = [EaseSDKHelper sendImageMessageWithImage:image
  1805. to:self.conversation.conversationId
  1806. messageType:[self _messageTypeFromConversationType]
  1807. messageExt:nil];
  1808. [self _sendMessage:message];
  1809. }
  1810. - (void)sendVoiceMessageWithLocalPath:(NSString *)localPath
  1811. duration:(NSInteger)duration
  1812. {
  1813. id progress = nil;
  1814. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
  1815. progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVoice];
  1816. }
  1817. else{
  1818. progress = self;
  1819. }
  1820. EMMessage *message = [EaseSDKHelper sendVoiceMessageWithLocalPath:localPath
  1821. duration:duration
  1822. to:self.conversation.conversationId
  1823. messageType:[self _messageTypeFromConversationType]
  1824. messageExt:nil];
  1825. [self _sendMessage:message];
  1826. }
  1827. - (void)sendVideoMessageWithURL:(NSURL *)url
  1828. {
  1829. id progress = nil;
  1830. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:progressDelegateForMessageBodyType:)]) {
  1831. progress = [_dataSource messageViewController:self progressDelegateForMessageBodyType:EMMessageBodyTypeVideo];
  1832. }
  1833. else{
  1834. progress = self;
  1835. }
  1836. EMMessage *message = [EaseSDKHelper sendVideoMessageWithURL:url
  1837. to:self.conversation.conversationId
  1838. messageType:[self _messageTypeFromConversationType]
  1839. messageExt:nil];
  1840. [self _sendMessage:message];
  1841. }
  1842. - (void)sendFileMessageWith:(EMMessage *)message {
  1843. [self _sendMessage:message];
  1844. }
  1845. #pragma mark - notifycation
  1846. - (void)didBecomeActive
  1847. {
  1848. self.messageTimeIntervalTag = -1;
  1849. self.dataArray = [[self formatMessages:self.messsagesSource] mutableCopy];
  1850. [self.tableView reloadData];
  1851. if (self.isViewDidAppear)
  1852. {
  1853. NSMutableArray *unreadMessages = [NSMutableArray array];
  1854. for (EMMessage *message in self.messsagesSource)
  1855. {
  1856. if ([self shouldSendHasReadAckForMessage:message read:NO])
  1857. {
  1858. [unreadMessages addObject:message];
  1859. }
  1860. }
  1861. if ([unreadMessages count])
  1862. {
  1863. [self _sendHasReadResponseForMessages:unreadMessages isRead:YES];
  1864. }
  1865. [_conversation markAllMessagesAsRead:nil];
  1866. if (self.dataSource && [self.dataSource respondsToSelector:@selector(messageViewControllerMarkAllMessagesAsRead:)]) {
  1867. [self.dataSource messageViewControllerMarkAllMessagesAsRead:self];
  1868. }
  1869. }
  1870. }
  1871. - (void)hideImagePicker
  1872. {
  1873. if (_imagePicker && [EaseSDKHelper shareHelper].isShowingimagePicker) {
  1874. [_imagePicker dismissViewControllerAnimated:NO completion:nil];
  1875. }
  1876. }
  1877. #pragma mark - private
  1878. - (void)_reloadTableViewDataWithMessage:(EMMessage *)message
  1879. {
  1880. if ([self.conversation.conversationId isEqualToString:message.conversationId])
  1881. {
  1882. for (int i = 0; i < self.dataArray.count; i ++) {
  1883. id object = [self.dataArray objectAtIndex:i];
  1884. if ([object isKindOfClass:[EaseMessageModel class]]) {
  1885. id<IMessageModel> model = object;
  1886. if ([message.messageId isEqualToString:model.messageId]) {
  1887. id<IMessageModel> model = nil;
  1888. if (self.dataSource && [self.dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
  1889. model = [self.dataSource messageViewController:self modelForMessage:message];
  1890. }
  1891. else{
  1892. model = [[EaseMessageModel alloc] initWithMessage:message];
  1893. model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
  1894. model.failImageName = @"imageDownloadFail";
  1895. }
  1896. [self.tableView beginUpdates];
  1897. [self.dataArray replaceObjectAtIndex:i withObject:model];
  1898. [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
  1899. [self.tableView endUpdates];
  1900. break;
  1901. }
  1902. }
  1903. }
  1904. }
  1905. }
  1906. - (void)_updateMessageStatus:(EMMessage *)aMessage
  1907. {
  1908. BOOL isChatting = [aMessage.conversationId isEqualToString:self.conversation.conversationId];
  1909. if (aMessage && isChatting) {
  1910. id<IMessageModel> model = nil;
  1911. if (_dataSource && [_dataSource respondsToSelector:@selector(messageViewController:modelForMessage:)]) {
  1912. model = [_dataSource messageViewController:self modelForMessage:aMessage];
  1913. }
  1914. else{
  1915. model = [[EaseMessageModel alloc] initWithMessage:aMessage];
  1916. model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
  1917. model.failImageName = @"imageDownloadFail";
  1918. }
  1919. if (model) {
  1920. __block NSUInteger index = NSNotFound;
  1921. [self.dataArray enumerateObjectsUsingBlock:^(EaseMessageModel *model, NSUInteger idx, BOOL *stop){
  1922. if ([model conformsToProtocol:@protocol(IMessageModel)]) {
  1923. if ([aMessage.messageId isEqualToString:model.message.messageId])
  1924. {
  1925. index = idx;
  1926. *stop = YES;
  1927. }
  1928. }
  1929. }];
  1930. if (index != NSNotFound)
  1931. {
  1932. [self.dataArray replaceObjectAtIndex:index withObject:model];
  1933. [self.tableView beginUpdates];
  1934. [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
  1935. [self.tableView endUpdates];
  1936. }
  1937. }
  1938. }
  1939. }
  1940. - (NSArray*)_searchAtTargets:(NSString*)text
  1941. {
  1942. NSMutableArray *targets = nil;
  1943. if (text.length > 1) {
  1944. targets = [NSMutableArray array];
  1945. NSArray *splits = [text componentsSeparatedByString:@"@"];
  1946. if ([splits count]) {
  1947. for (NSString *split in splits) {
  1948. if (split.length) {
  1949. NSString *atALl = NSEaseLocalizedString(@"group.atAll", @"all");
  1950. if (split.length >= atALl.length && [split compare:atALl options:NSCaseInsensitiveSearch range:NSMakeRange(0, atALl.length)] == NSOrderedSame) {
  1951. [targets removeAllObjects];
  1952. [targets addObject:kGroupMessageAtAll];
  1953. return targets;
  1954. }
  1955. for (EaseAtTarget *target in self.atTargets) {
  1956. if ([target.userId length]) {
  1957. if ([split hasPrefix:target.userId] || (target.nickname && [split hasPrefix:target.nickname])) {
  1958. [targets addObject:target.userId];
  1959. [self.atTargets removeObject:target];
  1960. break;
  1961. }
  1962. }
  1963. }
  1964. }
  1965. }
  1966. }
  1967. }
  1968. return targets;
  1969. }
  1970. @end