// // ChatViewController.m // LekangGuard // // Created by ecell on 2023/1/3. // #import "ChatViewController.h" #import #import "AppDelegate.h" #import "XHDisplayTextViewController.h" #import "XHDisplayMediaViewController.h" #import "XHDisplayLocationViewController.h" #import "XHAudioPlayerHelper.h" #import "LatestLocationModel.h" #import "CJFileUtility.h" #import "VoiceConverter.h" #import @interface ChatViewController () @property (nonatomic, strong) NSArray *emotionManagers; @property (nonatomic ,strong) UIBarButtonItem *rightBarBtn; @property (nonatomic ,assign) NSInteger page; @end @implementation ChatViewController - (void)viewDidLoad { [super viewDidLoad]; self.allowsSendMultiMedia = NO; self.allowsSendVoice = YES; self.page = 1; self.view.backgroundColor = RGB(241, 241, 241); self.zx_navTitle = GJText(@"微聊"); kWeakSelf(self) [self zx_setRightBtnWithImg:ImageName_(@"icon_delect_1") clickedBlock:^(ZXNavItemBtn * _Nonnull btn) { EasyAlertView *alertV = [EasyAlertView alertViewWithTitle:GJText(@"是否清除聊天信息") subtitle:nil AlertViewType:AlertViewTypeSystemAlert config:nil]; [alertV addAlertItem:^EasyAlertItem *{ return [EasyAlertItem itemWithTitle:GJText(@"取消") type:AlertItemTypeSystemCancel callback:nil]; }]; [alertV addAlertItem:^EasyAlertItem *{ return [EasyAlertItem itemWithTitle:GJText(@"确定") type:AlertItemTypeSystemDefault callback:^(EasyAlertView *showview, long index) { [weakself DeleteChatMessage]; }]; }]; [alertV showAlertView]; }]; self.messageInputView.multiMediaSendButton.hidden = YES; self.messageInputView.faceSendButton.hidden = YES; self.messageInputView.inputTextView.hidden = YES; self.messageInputView.voiceChangeButton.hidden = YES; // UIApplication *app = [UIApplication sharedApplication]; // [[NSNotificationCenter defaultCenter] addObserver:self // selector:@selector(applicationWillResignActive:) // name:UIApplicationWillResignActiveNotification // object:app]; // // [[NSNotificationCenter defaultCenter] addObserver:self // selector:@selector(applicationDidBecomeActive:) // name:UIApplicationDidBecomeActiveNotification // object:app]; // //MARK :防止在ios 11 的系统中刷新Tableview 之后的抖动 self.messageTableView.frame = CGRectMake(0, iPhoneX_TabbarHeight, SCREEN_WIDTH, SCREEN_HEIGHT-iPhoneX_NavHeight); self.messageTableView.estimatedRowHeight = 0; self.messageTableView.estimatedSectionHeaderHeight = 0; self.messageTableView.estimatedSectionFooterHeight = 0; [self.messageTableView setBackgroundColor:KKClearColor]; [self.messageTableView bindHeadRefreshHandler:^{ [UICommon feedbackGenerator]; [weakself QueryChatListModel:TableisReload_h pageSize:KSizeNO current:self.page]; } themeColor:KKMainColor refreshStyle:KafkaRefreshStyleReplicatorWoody]; [UICommon MessageUpload:@"加载中"]; [weakself.messageTableView.headRefreshControl beginRefreshing]; } /// 收到推送聊天信息 - (void)pusChatMessage:(NSDictionary *)chatModel { NSDictionary *dic = chatModel[@"message"]; NSString *uid = dic[@"senderID"]; /// 防止同设备两个不同账号切换后,消息推送重复拉取数据 if (![uid isEqualToString:APIManager.sharedManager.loginModel.openid]) [self QueryChatListModel:TableisReload_f pageSize:1 current:1]; } #pragma mark 查询微聊数据 /// 查询微聊数据 - (void)QueryChatListModel:(TableisReload)isReload pageSize:(NSInteger)pageSize current:(NSInteger)current { kWeakSelf(self) NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters setValue:@(current) forKey:@"current"]; [parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; [parameters setValue:@(pageSize) forKey:@"pageSize"]; [[[APIManager sharedManager] APGET:QueryChat_URL parameters:parameters resultClass:BaseModel.class] subscribeNext:^(BaseModel *model) { [UICommon HidenLoading]; if (isReload == TableisReload_d) [weakself.modelListArr removeAllObjects]; if (model.records.count > 0) { NSMutableArray *messageArr = [NSMutableArray arrayWithArray:[[model.records reverseObjectEnumerator] allObjects]];//倒序操作 [messageArr enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) { XHMessage *message = [weakself ModelConversion:obj]; if (isReload == TableisReload_f && messageArr.count > 0) [self.modelListArr insertObject:message atIndex:self.modelListArr.count]; else [self.modelListArr insertObject:message atIndex:idx]; XHMessage *msg = weakself.modelListArr[idx]; NSLog(@"%@",[UICommon getDateFormatWithStr:@"YYYY-MM-dd HH:mm:ss" date:msg.timestamp]); }]; self.page ++; //NSLog(@"%@",messageArr); } weakself.messages = weakself.modelListArr; NSInteger rows = 0; if (weakself.modelListArr.count > 0) { rows = isReload == TableisReload_f ? model.records.count - 1 : weakself.modelListArr.count-1; if (isReload == TableisReload_h && model.records.count <= 0) rows = 0; } [self reloadTable:rows]; self.zx_navRightBtn.hidden = weakself.modelListArr.count > 0 ? NO : YES; [weakself.messageTableView.headRefreshControl endRefreshing]; } error:^(NSError * _Nullable error) { NSDictionary *dic = error.userInfo; [UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; }]; } /** * 发送语音消息的回调方法 * * @param voicePath 目标语音本地路径 * @param voiceDuration 目标语音时长 * @param sender 发送者的名字 * @param date 发送时间 */ - (void)didSendVoice:(NSString *)voicePath voiceDuration:(NSNumber *)voiceDuration fromSender:(NSString *)sender onDate:(NSDate *)date { if ([voiceDuration doubleValue] < 1) { [UICommon MessageErrorText:@"录音时间太短"]; return; } XHMessage *message = [[XHMessage alloc] initWithVoicePath:voicePath voiceUrl:nil voiceDuration:F(@"%@", voiceDuration) sender:sender timestamp:date]; // message.avatar = ImageName_(APIManager.sharedManager.deviceModel.image); message.avatarUrl = APIManager.sharedManager.deviceModel.image; message.senderID = APIManager.sharedManager.loginModel.openid; [self wavToAmr:voicePath]; voicePath = [CJFileUtility changeFileExt:voicePath ext:@"amr"]; kWeakSelf(self) NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters setValue:@(voiceDuration.integerValue) forKey:@"duration"]; [parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; [parameters setValue:@(3) forKey:@"myType"]; // [parameters setValue:@"" forKey:@"content"]; [[[APIManager sharedManager] uploadFileWithURL:AddChat_URL parameters:parameters filePath:voicePath fileData:nil] subscribeNext:^(NSArray *x) { [UICommon HidenLoading]; XHMessage *model = [self ModelConversion:x[0]]; NSString *fileName = [NSString stringWithFormat:@"%@.wav",model.Id]; NSString *wavPath = [CJFileUtility documentsPathSubDir:@"msg_audio" andFile:fileName]; [CJFileUtility moveFile:[CJFileUtility changeFileExt:voicePath ext:@"wav"] to:wavPath]; model.voicePath = wavPath; [weakself.modelListArr addObject:model]; weakself.messages = weakself.modelListArr; [self reloadTable:weakself.modelListArr.count-1]; //[weakself addMessage:model]; self.zx_navRightBtn.hidden = weakself.modelListArr.count > 0 ? NO : YES; [weakself finishSendMessageWithBubbleMessageType:XHBubbleMessageMediaTypeVoice]; } error:^(NSError * _Nullable error) { NSDictionary *dic = error.userInfo; [UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; }]; } /** * 按下錄音按鈕 "準備" 錄音 */ - (void)prepareRecordingVoiceActionWithCompletion:(BOOL (^)(void))completion { WEAKSELF [weakSelf.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; [[XHAudioPlayerHelper shareInstance] stopAudio]; [super prepareRecordingVoiceActionWithCompletion:completion]; } /** * 配置Cell的样式或者字体 * * @param cell 目标Cell * @param indexPath 目标Cell所在位置IndexPath */ - (void)configureCell:(XHMessageTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { XHMessage *message = self.modelListArr[indexPath.row]; [cell.avatarButton sd_setImageWithURL:[NSURL URLWithString:message.avatarUrl] forState:0 placeholderImage:message.sendType == 1 ? ImageName_(@"icon_unclick_waigong_head_portrait") : ImageName_(@"icon_boy_head_portrait")]; if (self.currentSelectedCell == cell && [[XHAudioPlayerHelper shareInstance] isPlaying]) { [self.currentSelectedCell.messageBubbleView.animationVoiceImageView startAnimating]; }else{ [self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; } } /** * 点击多媒体消息的时候统一触发这个回调 * * @param message 被操作的目标消息Model * @param indexPath 该目标消息在哪个IndexPath里面 * @param messageTableViewCell 目标消息在该Cell上 */ - (void)multiMediaMessageDidSelectedOnMessage:(id )message atIndexPath:(NSIndexPath *)indexPath onMessageTableViewCell:(XHMessageTableViewCell *)messageTableViewCell { kWeakSelf(self) if (!message) { [self.messageInputView.inputTextView resignFirstResponder]; return; } UIViewController *disPlayViewController; switch (message.messageMediaType) { case XHBubbleMessageMediaTypeVideo: case XHBubbleMessageMediaTypePhoto: { XHDisplayMediaViewController *messageDisplayTextView = [[XHDisplayMediaViewController alloc] init]; messageDisplayTextView.message = message; disPlayViewController = messageDisplayTextView; break; } break; case XHBubbleMessageMediaTypeVoice: { DLog(@"message : %@", message.voicePath); messageTableViewCell.messageBubbleView.voiceUnreadDotImageView.hidden = YES; [[XHAudioPlayerHelper shareInstance] setDelegate:(id)self]; if (self.currentSelectedCell) { [self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; } if (self.currentSelectedCell == messageTableViewCell) { [messageTableViewCell.messageBubbleView.animationVoiceImageView stopAnimating]; [[XHAudioPlayerHelper shareInstance] stopAudio]; self.currentSelectedCell = nil; } else { self.currentSelectedCell = messageTableViewCell; [weakself P_playVoiceWithMessage:message block:^(NSString *wavPath) { if (weakself.currentSelectedCell == messageTableViewCell){ [messageTableViewCell.messageBubbleView.animationVoiceImageView startAnimating]; [[XHAudioPlayerHelper shareInstance] managerAudioWithFileName:wavPath toPlay:YES]; if (message.bubbleMessageType == 1 && message.isRead == NO) [weakself UpdateByIdMessage:message]; } }]; } break; } break; case XHBubbleMessageMediaTypeEmotion: DLog(@"facePath : %@", message.emotionPath); break; case XHBubbleMessageMediaTypeLocalPosition: { // DLog(@"facePath : %@", message.localPositionPhoto); LatestLocationModel *locate = [[LatestLocationModel alloc] init]; locate.longitude = @([message location].coordinate.longitude); locate.latitude = @([message location].coordinate.latitude); locate.addr = [message geolocations]; locate.updateTime = [UICommon getDateFormatWithStr:@"YYYY-MM-dd HH:mm:ss" date:message.timestamp]; break; } default: break; } if (disPlayViewController) { [self.navigationController pushViewController:disPlayViewController animated:YES]; } } #pragma 播放语音 - (void)P_playVoiceWithMessage:(XHMessage *)message block:(void(^)(NSString *wavPath))block { NSString *path = [CJFileUtility documentsPathSubDir:@"msg_audio" andFile:message.Id]; NSString *wavPath = [path stringByAppendingPathExtension:@"wav"]; NSString *amrPath = [path stringByAppendingPathExtension:@"amr"]; if ([CJFileUtility fileExists:wavPath] || [CJFileUtility fileExists:amrPath]) { if (![CJFileUtility fileExists:wavPath] && [CJFileUtility fileExists:amrPath]) { [self amrToWav:amrPath]; } block (wavPath); } else { WEAKSELF NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:message.text]]; AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { /* 设定下载到的位置 */ return [NSURL fileURLWithPath:amrPath]; } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { NSLog(@"下载完成"); if (![CJFileUtility fileExists:wavPath] && [CJFileUtility fileExists:amrPath]) { [weakSelf amrToWav:amrPath]; } block(wavPath); }]; [downloadTask resume]; } } #pragma mark XHAudioPlayerHelperDelegate - (void)didAudioPlayerStopPlay:(AVAudioPlayer *)audioPlayer { if (!self.currentSelectedCell) { return; } [self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; self.currentSelectedCell = nil; } #pragma mark 根据消息id更新当前消息为已读状态 /// 根据消息id更新当前消息为已读状态 - (void)UpdateByIdMessage:(XHMessage *)message { NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters setValue:message.Id forKey:@"id"]; [[[APIManager sharedManager] APGET:UpdateById_URL parameters:parameters resultClass:nil] subscribeNext:^(id _Nullable x) { [UICommon HidenLoading]; message.isRead = YES; self.messageCoutnUpdata(); } error:^(NSError * _Nullable error) { NSDictionary *dic = error.userInfo; [UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; }]; } #pragma mark 删除微聊数据 /// 删除微聊数据 - (void)DeleteChatMessage { [UICommon MessageUpload:@"加载中"]; NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; [[[APIManager sharedManager] APGET:DeleteChat_URL parameters:parameters resultClass:nil] subscribeNext:^(id _Nullable x) { [UICommon HidenLoading]; [UICommon MessageSuccessText:@"删除成功"]; self.page = 1; [self QueryChatListModel:TableisReload_d pageSize:KSizeNO current:1]; } error:^(NSError * _Nullable error) { NSDictionary *dic = error.userInfo; [UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; }]; } /// 刷新table /// @param row 滚动到指定行 - (void)reloadTable:(NSInteger)row { [self.messageTableView reloadData]; if (self.modelListArr.count > 0) [self.messageTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; } /// 本地数据模型转XHMessage /// @param obj 数据 - (XHMessage *)ModelConversion:(id)obj { XHMessage *message = [[XHMessage alloc] init]; ChatModel *chatModel = [ChatModel yy_modelWithJSON:obj]; message.text = chatModel.content; message.isRead = chatModel.readStatus; message.timestamp = [UICommon getTimeStrWithString:chatModel.createTime]; message.avatarUrl = chatModel.image; message.voiceDuration = chatModel.duration; message.messageMediaType = chatModel.myType; message.senderID = chatModel.sendUser; message.bubbleMessageType = [chatModel.sendUser isEqualToString:APIManager.sharedManager.loginModel.openid] ? 0 : 1 ; message.sender = chatModel.nickName; message.Id = chatModel.Id; message.sendType = chatModel.sendType; if (chatModel.image.length <= 0 && [chatModel.sendUser isEqualToString:APIManager.sharedManager.loginModel.openid]) message.avatarUrl = APIManager.sharedManager.userModel.image; return message; } - (void)wavToAmr:(NSString *)path { //转格式 if ([CJFileUtility fileExists:path]) { NSString *armPath = [CJFileUtility changeFileExt:path ext:@"amr"]; [VoiceConverter wavToAmr:path amrSavePath:armPath]; } } //转换后删掉amr文件 - (void)amrToWav:(NSString *)path { //转格式 if ([CJFileUtility fileExists:path]) { NSString *wavPath = [CJFileUtility changeFileExt:path ext:@"wav"]; [VoiceConverter amrToWav:path wavSavePath:wavPath]; [CJFileUtility deleteFile:path]; } } @end /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */