EaseMessageViewController.m 85 KB

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