You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

479 lines
19 KiB

2 years ago
//
// ChatViewController.m
// LekangGuard
//
// Created by ecell on 2023/1/3.
//
#import "ChatViewController.h"
#import <SDWebImage/UIButton+WebCache.h>
#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 <AVFoundation/AVFoundation.h>
@interface ChatViewController () <XHAudioPlayerHelperDelegate,UIActionSheetDelegate>
@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 <XHMessageModel>)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<NSFileManagerDelegate>)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.
}
*/